Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions editor/src/node_graph_executor/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -491,8 +491,8 @@ impl NodeRuntime {
let mut render = SvgRender::new();
graphic.render_svg(&mut render, &render_params);

// And give the SVG a viewbox and outer <svg>...</svg> wrapper tag
render.format_svg(bounds[0], bounds[1]);
// And give the SVG a viewbox and outer <svg>...</svg> wrapper tag as isolation group to prevent blending against the background checker
render.format_svg_with_attributes(bounds[0], bounds[1], |attributes| attributes.push("style", "isolation: isolate;"));

render.svg
};
Expand Down
57 changes: 39 additions & 18 deletions node-graph/libraries/rendering/src/renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,20 @@ impl SvgRender {

/// Add an outer `<svg>...</svg>` tag with a `viewBox` and the `<defs />`
pub fn format_svg(&mut self, bounds_min: DVec2, bounds_max: DVec2) {
self.format_svg_with_attributes(bounds_min, bounds_max, |_| {});
}

/// Add an outer `<svg>...</svg>` tag with a `viewBox`, the `<defs />`, and the provided attributes.
pub fn format_svg_with_attributes(&mut self, bounds_min: DVec2, bounds_max: DVec2, attributes: impl FnOnce(&mut SvgRenderAttrs)) {
let (x, y) = bounds_min.into();
let (size_x, size_y) = (bounds_max - bounds_min).into();

let mut attr_render = SvgRender::new();
attributes(&mut SvgRenderAttrs(&mut attr_render));
let attributes_str = attr_render.svg.to_svg_string();

let svg_header = format!(
r#"<svg xmlns="http://www.w3.org/2000/svg" xmlns:graphite="https://graphite.art" viewBox="{x} {y} {size_x} {size_y}"><defs>{defs}</defs>"#,
r#"<svg xmlns="http://www.w3.org/2000/svg" xmlns:graphite="https://graphite.art" viewBox="{x} {y} {size_x} {size_y}"{attributes_str}><defs>{defs}</defs>"#,
defs = &self.svg_defs
);
self.svg_defs = String::new();
Expand Down Expand Up @@ -708,22 +718,10 @@ impl Render for List<Artboard> {
let Some(content) = self.element(index).map(Artboard::as_graphic_list) else { continue };
let (location, dimensions, background, clip) = read_artboard_attributes(self, index);

let x = location.x.min(location.x + dimensions.x);
let y = location.y.min(location.y + dimensions.y);
let width = dimensions.x.abs();
let height = dimensions.y.abs();

// Background
render.leaf_tag("rect", |attributes| {
attributes.push("fill", format!("#{}", SRGBA8::from(background).to_rgb_hex()));
if background.a() < 1. {
attributes.push("fill-opacity", ((background.a() * 1000.).round() / 1000.).to_string());
}
attributes.push("x", x.to_string());
attributes.push("y", y.to_string());
attributes.push("width", width.to_string());
attributes.push("height", height.to_string());
});
let local_x = dimensions.x.min(0.);
let local_y = dimensions.y.min(0.);

// Artwork
render.parent_tag(
Expand All @@ -742,15 +740,38 @@ impl Render for List<Artboard> {

write!(
&mut attributes.0.svg_defs,
r##"<clipPath id="{id}"><rect x="0" y="0" width="{}" height="{}" /></clipPath>"##,
dimensions.x, dimensions.y,
r##"<clipPath id="{id}"><rect x="{local_x}" y="{local_y}" width="{width}" height="{height}" /></clipPath>"##,
)
.unwrap();
attributes.push("clip-path", selector);
} else {
// Keep blend behavior consistent between clipped and unclipped artboards.
// An SVG clip-path creates an isolated group, so blends inside it do not use external backdrops such as the checkerboard preview background.
// Without a clip-path, the group would otherwise blend against that backdrop, so we explicitly isolate the artboard group.
// TODO: Consider another way to clip artboards.
// With multiple transparent artboards, blending does not take colors from other artboards into account, which is inconsistent from the vello's behavior.
attributes.push("style", "isolation: isolate;");
}
},
// Artwork content
|render| {
// Background
render.leaf_tag("rect", |attributes| {
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
if local_x != 0. {
attributes.push("x", local_x.to_string());
}
if local_y != 0. {
attributes.push("y", local_y.to_string());
}

attributes.push("fill", format!("#{}", SRGBA8::from(background).to_rgb_hex()));
if background.a() < 1. {
attributes.push("fill-opacity", ((background.a() * 1000.).round() / 1000.).to_string());
}
attributes.push("width", width.to_string());
attributes.push("height", height.to_string());
});
Comment thread
YohYamasaki marked this conversation as resolved.

// Artwork content
let mut render_params = render_params.clone();
render_params.artboard_background = Some(background);
content.render_svg(render, &render_params);
Expand Down
Loading