[librsvg] drawing_ctx: add draw_from_use_node method
- From: Paolo Borelli <pborelli src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [librsvg] drawing_ctx: add draw_from_use_node method
- Date: Mon, 6 Jan 2020 09:47:39 +0000 (UTC)
commit 2b58cbf8d1bd0c3ab6cf4e0e6f1db69139629365
Author: Paolo Borelli <pborelli gnome org>
Date: Thu Jan 2 18:44:20 2020 +0100
drawing_ctx: add draw_from_use_node method
Move the logic required to draw a node specified through the
<use> element inside the drawing context.
rsvg_internals/src/drawing_ctx.rs | 109 +++++++++++++++++++++++++++-
rsvg_internals/src/structure.rs | 149 ++++++++++----------------------------
2 files changed, 144 insertions(+), 114 deletions(-)
---
diff --git a/rsvg_internals/src/drawing_ctx.rs b/rsvg_internals/src/drawing_ctx.rs
index 7d8bc367..377ca997 100644
--- a/rsvg_internals/src/drawing_ctx.rs
+++ b/rsvg_internals/src/drawing_ctx.rs
@@ -24,10 +24,11 @@ use crate::paint_server::{PaintServer, PaintSource};
use crate::pattern::Pattern;
use crate::properties::ComputedValues;
use crate::property_defs::{
- ClipRule, FillRule, Opacity, ShapeRendering, StrokeDasharray, StrokeLinecap, StrokeLinejoin,
+ ClipRule, FillRule, Opacity, Overflow, ShapeRendering, StrokeDasharray, StrokeLinecap,
+ StrokeLinejoin,
};
use crate::rect::{Rect, TransformRect};
-use crate::structure::{ClipPath, Mask};
+use crate::structure::{ClipPath, Mask, Symbol, Use};
use crate::surface_utils::{shared_surface::SharedImageSurface, shared_surface::SurfaceType};
use crate::unit_interval::UnitInterval;
use crate::viewbox::ViewBox;
@@ -332,7 +333,7 @@ impl DrawingCtx {
self.acquired_nodes.acquire(fragment, node_types)
}
- pub fn acquire_node_ref(&mut self, node: &RsvgNode) -> Result<AcquiredNode, AcquireError> {
+ fn acquire_node_ref(&mut self, node: &RsvgNode) -> Result<AcquiredNode, AcquireError> {
self.acquired_nodes.push_node_ref(node)
}
@@ -1002,6 +1003,108 @@ impl DrawingCtx {
res
}
+
+ pub fn draw_from_use_node(
+ &mut self,
+ node: &RsvgNode,
+ cascaded: &CascadedValues<'_>,
+ clipping: bool,
+ ) -> Result<BoundingBox, RenderingError> {
+ let node_data = node.borrow();
+ let use_ = node_data.get_impl::<Use>();
+
+ // <use> is an element that is used directly, unlike
+ // <pattern>, which is used through a fill="url(#...)"
+ // reference. However, <use> will always reference another
+ // element, potentially itself or an ancestor of itself (or
+ // another <use> which references the first one, etc.). So,
+ // we acquire the <use> element itself so that circular
+ // references can be caught.
+ let _self_acquired = self.acquire_node_ref(node).map_err(|e| {
+ if let AcquireError::CircularReference(_) = e {
+ rsvg_log!("circular reference in element {}", node);
+ RenderingError::CircularReference
+ } else {
+ unreachable!();
+ }
+ })?;
+
+ let link = use_.get_link();
+ if link.is_none() {
+ return Ok(self.empty_bbox());
+ }
+
+ let acquired = match self.acquire_node(link.unwrap(), &[]) {
+ Ok(acquired) => acquired,
+
+ Err(AcquireError::CircularReference(node)) => {
+ rsvg_log!("circular reference in element {}", node);
+ return Err(RenderingError::CircularReference);
+ }
+
+ Err(AcquireError::MaxReferencesExceeded) => {
+ return Err(RenderingError::InstancingLimit);
+ }
+
+ Err(AcquireError::InvalidLinkType(_)) => unreachable!(),
+
+ Err(AcquireError::LinkNotFound(fragment)) => {
+ rsvg_log!("element {} references nonexistent \"{}\"", node, fragment);
+ return Ok(self.empty_bbox());
+ }
+ };
+
+ let values = cascaded.get();
+ let params = self.get_view_params();
+ let use_rect = use_.get_rect(values, ¶ms);
+
+ // width or height set to 0 disables rendering of the element
+ // https://www.w3.org/TR/SVG/struct.html#UseElementWidthAttribute
+ if use_rect.is_empty() {
+ return Ok(self.empty_bbox());
+ }
+
+ let child = acquired.get();
+
+ if child.borrow().get_type() != NodeType::Symbol {
+ let cr = self.get_cairo_context();
+ cr.translate(use_rect.x0, use_rect.y0);
+
+ self.with_discrete_layer(node, values, clipping, &mut |dc| {
+ dc.draw_node_from_stack(
+ &CascadedValues::new_from_values(&child, values),
+ &child,
+ clipping,
+ )
+ })
+ } else {
+ let node_data = child.borrow();
+ let symbol = node_data.get_impl::<Symbol>();
+
+ let clip_mode = if !values.is_overflow()
+ || (values.overflow == Overflow::Visible && child.borrow().is_overflow())
+ {
+ Some(ClipMode::ClipToVbox)
+ } else {
+ None
+ };
+
+ self.with_discrete_layer(node, values, clipping, &mut |dc| {
+ let _params = dc.push_new_viewport(
+ symbol.get_viewbox(),
+ use_rect,
+ symbol.get_preserve_aspect_ratio(),
+ clip_mode,
+ );
+
+ child.draw_children(
+ &CascadedValues::new_from_values(&child, values),
+ dc,
+ clipping,
+ )
+ })
+ }
+ }
}
#[derive(Debug)]
diff --git a/rsvg_internals/src/structure.rs b/rsvg_internals/src/structure.rs
index c6b50af6..4bfa1d9c 100644
--- a/rsvg_internals/src/structure.rs
+++ b/rsvg_internals/src/structure.rs
@@ -9,13 +9,11 @@ use crate::coord_units::CoordUnits;
use crate::dpi::Dpi;
use crate::drawing_ctx::{ClipMode, DrawingCtx, ViewParams};
use crate::error::*;
-use crate::float_eq_cairo::ApproxEqCairo;
use crate::length::*;
use crate::node::*;
use crate::parsers::{Parse, ParseValue};
use crate::properties::ComputedValues;
use crate::property_bag::PropertyBag;
-use crate::property_defs::Overflow;
use crate::rect::Rect;
use crate::viewbox::*;
@@ -273,6 +271,33 @@ pub struct Use {
h: Option<Length<Vertical>>,
}
+impl Use {
+ pub fn get_link(&self) -> Option<&Fragment> {
+ self.link.as_ref()
+ }
+
+ pub fn get_rect(&self, values: &ComputedValues, params: &ViewParams) -> Rect {
+ let x = self.x.normalize(values, ¶ms);
+ let y = self.y.normalize(values, ¶ms);
+
+ // If attributes ‘width’ and/or ‘height’ are not specified,
+ // [...] use values of '100%' for these attributes.
+ // From https://www.w3.org/TR/SVG/struct.html#UseElement in
+ // "If the ‘use’ element references a ‘symbol’ element"
+
+ let w = self
+ .w
+ .unwrap_or_else(|| Length::<Horizontal>::parse_str("100%").unwrap())
+ .normalize(values, ¶ms);
+ let h = self
+ .h
+ .unwrap_or_else(|| Length::<Vertical>::parse_str("100%").unwrap())
+ .normalize(values, ¶ms);
+
+ Rect::new(x, y, x + w, y + h)
+ }
+}
+
impl NodeTrait for Use {
fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
for (attr, value) in pbag.iter() {
@@ -306,115 +331,7 @@ impl NodeTrait for Use {
draw_ctx: &mut DrawingCtx,
clipping: bool,
) -> Result<BoundingBox, RenderingError> {
- let values = cascaded.get();
-
- if self.link.is_none() {
- return Ok(draw_ctx.empty_bbox());
- }
-
- let link = self.link.as_ref().unwrap();
-
- // <use> is an element that is used directly, unlike
- // <pattern>, which is used through a fill="url(#...)"
- // reference. However, <use> will always reference another
- // element, potentially itself or an ancestor of itself (or
- // another <use> which references the first one, etc.). So,
- // we acquire the <use> element itself so that circular
- // references can be caught.
- let _self_acquired = draw_ctx.acquire_node_ref(node).map_err(|e| {
- if let AcquireError::CircularReference(_) = e {
- rsvg_log!("circular reference in element {}", node);
- RenderingError::CircularReference
- } else {
- unreachable!();
- }
- })?;
-
- let acquired = match draw_ctx.acquire_node(link, &[]) {
- Ok(acquired) => acquired,
-
- Err(AcquireError::CircularReference(node)) => {
- rsvg_log!("circular reference in element {}", node);
- return Err(RenderingError::CircularReference);
- }
-
- Err(AcquireError::MaxReferencesExceeded) => {
- return Err(RenderingError::InstancingLimit);
- }
-
- Err(AcquireError::InvalidLinkType(_)) => unreachable!(),
-
- Err(AcquireError::LinkNotFound(fragment)) => {
- rsvg_log!("element {} references nonexistent \"{}\"", node, fragment);
- return Ok(draw_ctx.empty_bbox());
- }
- };
-
- let child = acquired.get();
-
- let params = draw_ctx.get_view_params();
-
- let nx = self.x.normalize(values, ¶ms);
- let ny = self.y.normalize(values, ¶ms);
-
- // If attributes ‘width’ and/or ‘height’ are not specified,
- // [...] use values of '100%' for these attributes.
- // From https://www.w3.org/TR/SVG/struct.html#UseElement in
- // "If the ‘use’ element references a ‘symbol’ element"
-
- let nw = self
- .w
- .unwrap_or_else(|| Length::<Horizontal>::parse_str("100%").unwrap())
- .normalize(values, ¶ms);
- let nh = self
- .h
- .unwrap_or_else(|| Length::<Vertical>::parse_str("100%").unwrap())
- .normalize(values, ¶ms);
-
- // width or height set to 0 disables rendering of the element
- // https://www.w3.org/TR/SVG/struct.html#UseElementWidthAttribute
- if nw.approx_eq_cairo(0.0) || nh.approx_eq_cairo(0.0) {
- return Ok(draw_ctx.empty_bbox());
- }
-
- if child.borrow().get_type() != NodeType::Symbol {
- let cr = draw_ctx.get_cairo_context();
- cr.translate(nx, ny);
-
- draw_ctx.with_discrete_layer(node, values, clipping, &mut |dc| {
- dc.draw_node_from_stack(
- &CascadedValues::new_from_values(&child, values),
- &child,
- clipping,
- )
- })
- } else {
- let node_data = child.borrow();
- let symbol = node_data.get_impl::<Symbol>();
-
- let clip_mode = if !values.is_overflow()
- || (values.overflow == Overflow::Visible && child.borrow().is_overflow())
- {
- Some(ClipMode::ClipToVbox)
- } else {
- None
- };
-
- draw_ctx.with_discrete_layer(node, values, clipping, &mut |dc| {
- let _params = dc.push_new_viewport(
- symbol.vbox,
- Rect::new(nx, ny, nx + nw, ny + nh),
- symbol.preserve_aspect_ratio,
- clip_mode,
- );
-
- child.draw_children(
- &CascadedValues::new_from_values(&child, values),
- dc,
- clipping,
- )
- })
- }
+ draw_ctx.draw_from_use_node(node, cascaded, clipping)
}
}
@@ -424,6 +341,16 @@ pub struct Symbol {
vbox: Option<ViewBox>,
}
+impl Symbol {
+ pub fn get_viewbox(&self) -> Option<ViewBox> {
+ self.vbox
+ }
+
+ pub fn get_preserve_aspect_ratio(&self) -> AspectRatio {
+ self.preserve_aspect_ratio
+ }
+}
+
impl NodeTrait for Symbol {
fn set_atts(&mut self, _parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
for (attr, value) in pbag.iter() {
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]