[librsvg: 1/3] gradient: rework and simplify code
- From: Federico Mena Quintero <federico src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [librsvg: 1/3] gradient: rework and simplify code
- Date: Tue, 18 Jun 2019 14:08:18 +0000 (UTC)
commit 961a8b0852efe23900bfdc1ecbcbde3d012483c9
Author: Paolo Borelli <pborelli gnome org>
Date: Sun Jun 16 17:08:18 2019 +0200
gradient: rework and simplify code
Remove the need to match on an GradientVariant enum and factor
out a Resolve trait which can later be shared with Pattern.
rsvg_internals/src/gradient.rs | 743 ++++++++++++++++++-----------------------
1 file changed, 326 insertions(+), 417 deletions(-)
---
diff --git a/rsvg_internals/src/gradient.rs b/rsvg_internals/src/gradient.rs
index b63521d6..7345ec7e 100644
--- a/rsvg_internals/src/gradient.rs
+++ b/rsvg_internals/src/gradient.rs
@@ -75,96 +75,34 @@ impl From<SpreadMethod> for cairo::Extend {
}
}
-// Any of the attributes in gradient elements may be omitted. In turn, the missing
-// ones can be inherited from the gradient referenced by its "fallback" IRI. We
-// represent these possibly-missing attributes as Option<foo>.
-#[derive(Clone)]
-pub struct GradientCommon {
- pub units: Option<GradientUnits>,
- pub affine: Option<cairo::Matrix>,
- pub spread: Option<SpreadMethod>,
- pub fallback: Option<Fragment>,
- pub stops: Option<Vec<ColorStop>>,
-}
+// Any of the attributes in gradient elements may be omitted.
+// The missing ones are resolved from the gradient referenced
+// by its "fallback" IRI. If still missing, they are resolved
+// to the default value
+trait Resolve {
+ fn is_resolved(&self) -> bool;
-#[derive(Copy, Clone)]
-pub enum GradientVariant {
- Linear {
- x1: Option<LengthHorizontal>,
- y1: Option<LengthVertical>,
- x2: Option<LengthHorizontal>,
- y2: Option<LengthVertical>,
- },
-
- Radial {
- cx: Option<LengthHorizontal>,
- cy: Option<LengthVertical>,
- r: Option<LengthBoth>,
- fx: Option<LengthHorizontal>,
- fy: Option<LengthVertical>,
- },
-}
+ fn resolve_from_fallback(&mut self, fallback: &Self);
-#[derive(Clone)]
-pub struct Gradient {
- pub common: GradientCommon,
- pub variant: GradientVariant,
+ fn resolve_from_defaults(&mut self);
}
-impl Default for GradientCommon {
- fn default() -> GradientCommon {
- GradientCommon {
- units: Some(GradientUnits::default()),
- affine: Some(cairo::Matrix::identity()),
- spread: Some(SpreadMethod::default()),
- fallback: None,
- stops: Some(Vec::<ColorStop>::new()),
- }
- }
-}
-
-// All of the Gradient's fields are Option<foo> values, because
-// those fields can be omitted in the SVG file. We need to resolve
-// them to default values, or to fallback values that come from
-// another Gradient.
-//
-// For the fallback case, this would need something like
-//
-// if self.foo.is_none () { self.foo = fallback.foo; }
-//
-// And for the default case, it would be like
-// if self.foo.is_none () { self.foo = Some (default_value); }
-//
-// Both can be replaced by
-//
-// self.foo = self.foo.take ().or (bar);
-//
-// So we define a macro for that.
macro_rules! fallback_to (
($dest:expr, $default:expr) => (
$dest = $dest.take ().or ($default)
);
);
-impl GradientCommon {
- fn unresolved() -> GradientCommon {
- GradientCommon {
- units: None,
- affine: None,
- spread: None,
- fallback: None,
- stops: None,
- }
- }
-
- fn clone_stops(&self) -> Option<Vec<ColorStop>> {
- if let Some(ref stops) = self.stops {
- Some(stops.clone())
- } else {
- None
- }
- }
+#[derive(Clone, Default)]
+pub struct GradientCommon {
+ pub units: Option<GradientUnits>,
+ pub affine: Option<cairo::Matrix>,
+ pub spread: Option<SpreadMethod>,
+ pub fallback: Option<Fragment>,
+ pub stops: Option<Vec<ColorStop>>,
+}
+impl Resolve for GradientCommon {
fn is_resolved(&self) -> bool {
self.units.is_some()
&& self.affine.is_some()
@@ -172,11 +110,7 @@ impl GradientCommon {
&& self.stops.is_some()
}
- fn resolve_from_defaults(&mut self) {
- self.resolve_from_fallback(&GradientCommon::default());
- }
-
- fn resolve_from_fallback(&mut self, fallback: &GradientCommon) {
+ fn resolve_from_fallback(&mut self, fallback: &Self) {
fallback_to!(self.units, fallback.units);
fallback_to!(self.affine, fallback.affine);
fallback_to!(self.spread, fallback.spread);
@@ -185,6 +119,23 @@ impl GradientCommon {
self.fallback = fallback.fallback.clone();
}
+ fn resolve_from_defaults(&mut self) {
+ fallback_to!(self.units, Some(GradientUnits::default()));
+ fallback_to!(self.affine, Some(cairo::Matrix::identity()));
+ fallback_to!(self.spread, Some(SpreadMethod::default()));
+ fallback_to!(self.stops, Some(Vec::<ColorStop>::new()));
+ }
+}
+
+impl GradientCommon {
+ fn clone_stops(&self) -> Option<Vec<ColorStop>> {
+ if let Some(ref stops) = self.stops {
+ Some(stops.clone())
+ } else {
+ None
+ }
+ }
+
fn add_color_stop(
&mut self,
offset: UnitInterval,
@@ -217,152 +168,6 @@ impl GradientCommon {
unreachable!();
}
}
-}
-
-impl GradientVariant {
- fn unresolved_linear() -> Self {
- GradientVariant::Linear {
- x1: None,
- y1: None,
- x2: None,
- y2: None,
- }
- }
-
- fn unresolved_radial() -> Self {
- GradientVariant::Radial {
- cx: None,
- cy: None,
- r: None,
- fx: None,
- fy: None,
- }
- }
-
- fn is_resolved(&self) -> bool {
- match *self {
- GradientVariant::Linear { x1, y1, x2, y2 } => {
- x1.is_some() && y1.is_some() && x2.is_some() && y2.is_some()
- }
-
- GradientVariant::Radial { cx, cy, r, fx, fy } => {
- cx.is_some() && cy.is_some() && r.is_some() && fx.is_some() && fy.is_some()
- }
- }
- }
-
- fn default_linear() -> Self {
- // https://www.w3.org/TR/SVG/pservers.html#LinearGradients
-
- GradientVariant::Linear {
- x1: Some(LengthHorizontal::parse_str("0%").unwrap()),
- y1: Some(LengthVertical::parse_str("0%").unwrap()),
- x2: Some(LengthHorizontal::parse_str("100%").unwrap()),
- y2: Some(LengthVertical::parse_str("0%").unwrap()),
- }
- }
-
- fn default_radial() -> Self {
- // https://www.w3.org/TR/SVG/pservers.html#RadialGradients
-
- GradientVariant::Radial {
- cx: Some(LengthHorizontal::parse_str("50%").unwrap()),
- cy: Some(LengthVertical::parse_str("50%").unwrap()),
- r: Some(LengthBoth::parse_str("50%").unwrap()),
-
- fx: None,
- fy: None,
- }
- }
-
- fn resolve_from_defaults(&mut self) {
- // These are per the spec
- match *self {
- GradientVariant::Linear { .. } => {
- self.resolve_from_fallback(&GradientVariant::default_linear())
- }
-
- GradientVariant::Radial { .. } => {
- self.resolve_from_fallback(&GradientVariant::default_radial());
- }
- }
-
- if let GradientVariant::Radial {
- cx,
- cy,
- ref mut fx,
- ref mut fy,
- ..
- } = *self
- {
- // fx and fy fall back to the presentational value of cx and cy
- fallback_to!(*fx, cx);
- fallback_to!(*fy, cy);
- }
- }
-
- fn resolve_from_fallback(&mut self, fallback: &GradientVariant) {
- match *self {
- GradientVariant::Linear {
- ref mut x1,
- ref mut y1,
- ref mut x2,
- ref mut y2,
- } => {
- if let GradientVariant::Linear {
- x1: x1f,
- y1: y1f,
- x2: x2f,
- y2: y2f,
- } = *fallback
- {
- fallback_to!(*x1, x1f);
- fallback_to!(*y1, y1f);
- fallback_to!(*x2, x2f);
- fallback_to!(*y2, y2f);
- }
- }
-
- GradientVariant::Radial {
- ref mut cx,
- ref mut cy,
- ref mut r,
- ref mut fx,
- ref mut fy,
- } => {
- if let GradientVariant::Radial {
- cx: cxf,
- cy: cyf,
- r: rf,
- fx: fxf,
- fy: fyf,
- } = *fallback
- {
- fallback_to!(*cx, cxf);
- fallback_to!(*cy, cyf);
- fallback_to!(*r, rf);
- fallback_to!(*fx, fxf);
- fallback_to!(*fy, fyf);
- }
- }
- }
- }
-}
-
-impl Gradient {
- fn is_resolved(&self) -> bool {
- self.common.is_resolved() && self.variant.is_resolved()
- }
-
- fn resolve_from_defaults(&mut self) {
- self.common.resolve_from_defaults();
- self.variant.resolve_from_defaults();
- }
-
- fn resolve_from_fallback(&mut self, fallback: &Gradient) {
- self.common.resolve_from_fallback(&fallback.common);
- self.variant.resolve_from_fallback(&fallback.variant);
- }
fn add_color_stops_from_node(&mut self, node: &RsvgNode) {
assert!(
@@ -390,21 +195,12 @@ impl Gradient {
}
}
- fn add_color_stop(
- &mut self,
- offset: UnitInterval,
- rgba: cssparser::RGBA,
- opacity: UnitInterval,
- ) {
- self.common.add_color_stop(offset, rgba, opacity);
- }
-
fn add_color_stops_to_pattern<T, G: cairo::Gradient<PatternType = T>>(
&self,
pattern: &mut G,
opacity: &UnitInterval,
) {
- if let Some(stops) = self.common.stops.as_ref() {
+ if let Some(stops) = self.stops.as_ref() {
for stop in stops {
let UnitInterval(stop_offset) = stop.offset;
let &UnitInterval(o) = opacity;
@@ -421,8 +217,37 @@ impl Gradient {
}
}
+ fn set_on_pattern<P: cairo::PatternTrait + cairo::Gradient>(
+ &self,
+ pattern: &mut P,
+ bbox: &BoundingBox,
+ opacity: &UnitInterval,
+ ) {
+ let mut affine = self.affine.unwrap();
+ let units = self.units.unwrap();
+
+ if units == GradientUnits(CoordUnits::ObjectBoundingBox) {
+ let bbox_rect = bbox.rect.unwrap();
+ let bbox_matrix = cairo::Matrix::new(
+ bbox_rect.width,
+ 0.0,
+ 0.0,
+ bbox_rect.height,
+ bbox_rect.x,
+ bbox_rect.y,
+ );
+ affine = cairo::Matrix::multiply(&affine, &bbox_matrix);
+ }
+
+ affine.invert();
+ pattern.set_matrix(affine);
+ pattern.set_extend(cairo::Extend::from(self.spread.unwrap_or_default()));
+
+ self.add_color_stops_to_pattern(pattern, opacity);
+ }
+
fn bounds_are_valid(&self, bbox: &BoundingBox) -> bool {
- if self.common.units == Some(GradientUnits(CoordUnits::UserSpaceOnUse)) {
+ if self.units == Some(GradientUnits(CoordUnits::UserSpaceOnUse)) {
true
} else {
bbox.rect.map_or(false, |r| !r.is_empty())
@@ -430,52 +255,71 @@ impl Gradient {
}
}
-fn acquire_gradient<'a>(
- draw_ctx: &'a mut DrawingCtx,
- name: Option<&Fragment>,
-) -> Option<AcquiredNode> {
- name.and_then(move |fragment| draw_ctx.acquired_nodes().get_node(fragment))
- .and_then(|acquired| {
- let node_type = acquired.get().borrow().get_type();
+#[derive(Copy, Clone, Default)]
+pub struct GradientLinear {
+ x1: Option<LengthHorizontal>,
+ y1: Option<LengthVertical>,
+ x2: Option<LengthHorizontal>,
+ y2: Option<LengthVertical>,
+}
- if node_type == NodeType::LinearGradient || node_type == NodeType::RadialGradient {
- Some(acquired)
- } else {
- None
- }
- })
+impl Resolve for GradientLinear {
+ fn is_resolved(&self) -> bool {
+ self.x1.is_some() && self.y1.is_some() && self.x2.is_some() && self.y2.is_some()
+ }
+
+ fn resolve_from_fallback(&mut self, fallback: &Self) {
+ fallback_to!(self.x1, fallback.x1);
+ fallback_to!(self.y1, fallback.y1);
+ fallback_to!(self.x2, fallback.x2);
+ fallback_to!(self.y2, fallback.y2);
+ }
+
+ // https://www.w3.org/TR/SVG/pservers.html#LinearGradients
+ fn resolve_from_defaults(&mut self) {
+ fallback_to!(self.x1, Some(LengthHorizontal::parse_str("0%").unwrap()));
+ fallback_to!(self.y1, Some(LengthVertical::parse_str("0%").unwrap()));
+ fallback_to!(self.x2, Some(LengthHorizontal::parse_str("100%").unwrap()));
+ fallback_to!(self.y2, Some(LengthVertical::parse_str("0%").unwrap()));
+ }
}
-fn set_common_on_pattern<P: cairo::PatternTrait + cairo::Gradient>(
- gradient: &Gradient,
- pattern: &mut P,
- bbox: &BoundingBox,
- opacity: &UnitInterval,
-) {
- let mut affine = gradient.common.affine.unwrap();
-
- let units = gradient.common.units.unwrap();
-
- if units == GradientUnits(CoordUnits::ObjectBoundingBox) {
- let bbox_rect = bbox.rect.unwrap();
- let bbox_matrix = cairo::Matrix::new(
- bbox_rect.width,
- 0.0,
- 0.0,
- bbox_rect.height,
- bbox_rect.x,
- bbox_rect.y,
- );
- affine = cairo::Matrix::multiply(&affine, &bbox_matrix);
+#[derive(Copy, Clone, Default)]
+pub struct GradientRadial {
+ cx: Option<LengthHorizontal>,
+ cy: Option<LengthVertical>,
+ r: Option<LengthBoth>,
+ fx: Option<LengthHorizontal>,
+ fy: Option<LengthVertical>,
+}
+
+impl Resolve for GradientRadial {
+ fn is_resolved(&self) -> bool {
+ self.cx.is_some()
+ && self.cy.is_some()
+ && self.r.is_some()
+ && self.fx.is_some()
+ && self.fy.is_some()
+ }
+
+ fn resolve_from_fallback(&mut self, fallback: &Self) {
+ fallback_to!(self.cx, fallback.cx);
+ fallback_to!(self.cy, fallback.cy);
+ fallback_to!(self.r, fallback.r);
+ fallback_to!(self.fx, fallback.fx);
+ fallback_to!(self.fy, fallback.fy);
}
- affine.invert();
- pattern.set_matrix(affine);
- pattern.set_extend(cairo::Extend::from(
- gradient.common.spread.unwrap_or_default(),
- ));
+ // https://www.w3.org/TR/SVG/pservers.html#RadialGradients
+ fn resolve_from_defaults(&mut self) {
+ fallback_to!(self.cx, Some(LengthHorizontal::parse_str("50%").unwrap()));
+ fallback_to!(self.cy, Some(LengthVertical::parse_str("50%").unwrap()));
+ fallback_to!(self.r, Some(LengthBoth::parse_str("50%").unwrap()));
- gradient.add_color_stops_to_pattern(pattern, opacity);
+ // fx and fy fall back to the presentational value of cx and cy
+ fallback_to!(self.fx, self.cx);
+ fallback_to!(self.fy, self.cy);
+ }
}
// SVG defines radial gradients as being inside a circle (cx, cy, radius). The
@@ -555,138 +399,118 @@ impl NodeTrait for NodeStop {
}
}
-macro_rules! impl_paint_source {
+macro_rules! impl_resolve {
($gradient_type:ty) => {
- impl PaintSource for $gradient_type {
- type Source = Gradient;
-
- fn resolve(
- &self,
- node: &RsvgNode,
- draw_ctx: &mut DrawingCtx,
- bbox: &BoundingBox,
- ) -> Result<Option<Self::Source>, RenderingError> {
- let mut result = get_gradient_with_color_stops_from_node(node);
- let mut stack = NodeStack::new();
-
- while !result.is_resolved() {
- if let Some(acquired) = acquire_gradient(draw_ctx, result.common.fallback.as_ref()) {
- let a_node = acquired.get();
-
- if stack.contains(a_node) {
- rsvg_log!("circular reference in gradient {}", node);
- return Err(RenderingError::CircularReference);
- }
+ impl Resolve for $gradient_type {
+ fn is_resolved(&self) -> bool {
+ self.common.borrow().is_resolved() && self.variant.borrow().is_resolved()
+ }
- let fallback = get_gradient_with_color_stops_from_node(&a_node);
- result.resolve_from_fallback(&fallback);
+ fn resolve_from_fallback(&mut self, fallback: &$gradient_type) {
+ self.common
+ .borrow_mut()
+ .resolve_from_fallback(&fallback.common.borrow());
+ self.variant
+ .borrow_mut()
+ .resolve_from_fallback(&fallback.variant.borrow());
+ }
- stack.push(a_node);
- continue;
- }
+ fn resolve_from_defaults(&mut self) {
+ self.common.borrow_mut().resolve_from_defaults();
+ self.variant.borrow_mut().resolve_from_defaults();
+ }
+ }
+ };
+}
- result.resolve_from_defaults();
- }
+macro_rules! impl_paint_source_resolve {
+ ($gradient:ty, $node_type:pat, $other_gradient:ty, $other_type:pat) => {
+ fn resolve(
+ &self,
+ node: &RsvgNode,
+ draw_ctx: &mut DrawingCtx,
+ bbox: &BoundingBox,
+ ) -> Result<Option<Self::Source>, RenderingError> {
+ let mut result = self.clone();
+ result.common.borrow_mut().add_color_stops_from_node(node);
- if result.bounds_are_valid(bbox) {
- Ok(Some(result))
- } else {
- Ok(None)
- }
- }
+ let mut stack = NodeStack::new();
- fn set_pattern_on_draw_context(
- &self,
- gradient: &Self::Source,
- values: &ComputedValues,
- draw_ctx: &mut DrawingCtx,
- opacity: &UnitInterval,
- bbox: &BoundingBox,
- ) -> Result<bool, RenderingError> {
- assert!(gradient.is_resolved());
-
- let units = gradient.common.units.unwrap();
- let params = if units == GradientUnits(CoordUnits::ObjectBoundingBox) {
- draw_ctx.push_view_box(1.0, 1.0)
- } else {
- draw_ctx.get_view_params()
- };
+ while !result.is_resolved() {
+ let acquired = acquire_gradient(draw_ctx, result.common.borrow().fallback.as_ref());
- match gradient.variant {
- GradientVariant::Linear { x1, y1, x2, y2 } => {
- let mut pattern = cairo::LinearGradient::new(
- x1.as_ref().unwrap().normalize(values, ¶ms),
- y1.as_ref().unwrap().normalize(values, ¶ms),
- x2.as_ref().unwrap().normalize(values, ¶ms),
- y2.as_ref().unwrap().normalize(values, ¶ms),
- );
-
- let cr = draw_ctx.get_cairo_context();
- set_common_on_pattern(gradient, &mut pattern, bbox, opacity);
- cr.set_source(&cairo::Pattern::LinearGradient(pattern));
+ if let Some(acquired) = acquired {
+ let a_node = acquired.get();
+
+ if stack.contains(a_node) {
+ rsvg_log!("circular reference in gradient {}", node);
+ return Err(RenderingError::CircularReference);
}
- GradientVariant::Radial { cx, cy, r, fx, fy } => {
- let n_cx = cx.as_ref().unwrap().normalize(values, ¶ms);
- let n_cy = cy.as_ref().unwrap().normalize(values, ¶ms);
- let n_r = r.as_ref().unwrap().normalize(values, ¶ms);
- let n_fx = fx.as_ref().unwrap().normalize(values, ¶ms);
- let n_fy = fy.as_ref().unwrap().normalize(values, ¶ms);
+ match a_node.borrow().get_type() {
+ // Same type, resolve all attributes
+ $node_type => {
+ let fallback = a_node
+ .borrow()
+ .get_impl::<$gradient>()
+ .clone();
+ fallback.common.borrow_mut().add_color_stops_from_node(a_node);
+ result.resolve_from_fallback(&fallback);
+ }
+ // Other type of gradient, resolve common attributes
+ $other_type => {
+ let fallback = a_node
+ .borrow()
+ .get_impl::<$other_gradient>()
+ .clone();
+ fallback.common.borrow_mut().add_color_stops_from_node(a_node);
+ result.common.borrow_mut().resolve_from_fallback(&fallback.common.borrow());
+ }
+ _ => (),
+ }
- let (new_fx, new_fy) = fix_focus_point(n_fx, n_fy, n_cx, n_cy, n_r);
- let mut pattern = cairo::RadialGradient::new(new_fx, new_fy, 0.0, n_cx, n_cy, n_r);
+ stack.push(a_node);
- let cr = draw_ctx.get_cairo_context();
- set_common_on_pattern(gradient, &mut pattern, bbox, opacity);
- cr.set_source(&cairo::Pattern::RadialGradient(pattern));
- }
+ continue;
}
- Ok(true)
+ result.resolve_from_defaults();
+ }
+
+ if result.common.borrow().bounds_are_valid(bbox) {
+ Ok(Some(result))
+ } else {
+ Ok(None)
}
}
};
}
-fn get_gradient_with_color_stops_from_node(node: &RsvgNode) -> Gradient {
- let mut gradient = match node.borrow().get_type() {
- NodeType::LinearGradient => node
- .borrow()
- .get_impl::<NodeLinearGradient>()
- .gradient
- .borrow()
- .clone(),
- NodeType::RadialGradient => node
- .borrow()
- .get_impl::<NodeRadialGradient>()
- .gradient
- .borrow()
- .clone(),
- _ => unreachable!(),
- };
+fn acquire_gradient<'a>(
+ draw_ctx: &'a mut DrawingCtx,
+ name: Option<&Fragment>,
+) -> Option<AcquiredNode> {
+ name.and_then(move |fragment| draw_ctx.acquired_nodes().get_node(fragment))
+ .and_then(|acquired| {
+ let node_type = acquired.get().borrow().get_type();
- gradient.add_color_stops_from_node(node);
- gradient
+ if node_type == NodeType::LinearGradient || node_type == NodeType::RadialGradient {
+ Some(acquired)
+ } else {
+ None
+ }
+ })
}
+#[derive(Clone, Default)]
pub struct NodeLinearGradient {
- gradient: RefCell<Gradient>,
-}
-
-impl Default for NodeLinearGradient {
- fn default() -> NodeLinearGradient {
- NodeLinearGradient {
- gradient: RefCell::new(Gradient {
- common: GradientCommon::unresolved(),
- variant: GradientVariant::unresolved_linear(),
- }),
- }
- }
+ pub common: RefCell<GradientCommon>,
+ pub variant: RefCell<GradientLinear>,
}
impl NodeTrait for NodeLinearGradient {
fn set_atts(&self, _node: &RsvgNode, pbag: &PropertyBag<'_>) -> NodeResult {
- let mut g = self.gradient.borrow_mut();
+ let mut common = self.common.borrow_mut();
let mut x1 = None;
let mut y1 = None;
@@ -696,11 +520,11 @@ impl NodeTrait for NodeLinearGradient {
for (attr, value) in pbag.iter() {
match attr {
// Attributes common to linear and radial gradients
- local_name!("gradientUnits") => g.common.units = Some(attr.parse(value)?),
- local_name!("gradientTransform") => g.common.affine = Some(attr.parse(value)?),
- local_name!("spreadMethod") => g.common.spread = Some(attr.parse(value)?),
+ local_name!("gradientUnits") => common.units = Some(attr.parse(value)?),
+ local_name!("gradientTransform") => common.affine = Some(attr.parse(value)?),
+ local_name!("spreadMethod") => common.spread = Some(attr.parse(value)?),
local_name!("xlink:href") => {
- g.common.fallback = Some(Fragment::parse(value).attribute(attr)?)
+ common.fallback = Some(Fragment::parse(value).attribute(attr)?)
}
// Attributes specific to linear gradient
@@ -713,32 +537,69 @@ impl NodeTrait for NodeLinearGradient {
}
}
- g.variant = GradientVariant::Linear { x1, y1, x2, y2 };
+ *self.variant.borrow_mut() = GradientLinear { x1, y1, x2, y2 };
Ok(())
}
}
-impl_paint_source!(NodeLinearGradient);
+impl_resolve!(NodeLinearGradient);
-pub struct NodeRadialGradient {
- gradient: RefCell<Gradient>,
-}
+impl PaintSource for NodeLinearGradient {
+ type Source = NodeLinearGradient;
-impl Default for NodeRadialGradient {
- fn default() -> NodeRadialGradient {
- NodeRadialGradient {
- gradient: RefCell::new(Gradient {
- common: GradientCommon::unresolved(),
- variant: GradientVariant::unresolved_radial(),
- }),
- }
+ impl_paint_source_resolve!(
+ NodeLinearGradient,
+ NodeType::LinearGradient,
+ NodeRadialGradient,
+ NodeType::RadialGradient
+ );
+
+ fn set_pattern_on_draw_context(
+ &self,
+ gradient: &Self::Source,
+ values: &ComputedValues,
+ draw_ctx: &mut DrawingCtx,
+ opacity: &UnitInterval,
+ bbox: &BoundingBox,
+ ) -> Result<bool, RenderingError> {
+ assert!(gradient.is_resolved());
+
+ let units = gradient.common.borrow().units.unwrap();
+ let params = if units == GradientUnits(CoordUnits::ObjectBoundingBox) {
+ draw_ctx.push_view_box(1.0, 1.0)
+ } else {
+ draw_ctx.get_view_params()
+ };
+
+ let v = gradient.variant.borrow();
+ let mut pattern = cairo::LinearGradient::new(
+ v.x1.as_ref().unwrap().normalize(values, ¶ms),
+ v.y1.as_ref().unwrap().normalize(values, ¶ms),
+ v.x2.as_ref().unwrap().normalize(values, ¶ms),
+ v.y2.as_ref().unwrap().normalize(values, ¶ms),
+ );
+
+ let cr = draw_ctx.get_cairo_context();
+ gradient
+ .common
+ .borrow_mut()
+ .set_on_pattern(&mut pattern, bbox, opacity);
+ cr.set_source(&cairo::Pattern::LinearGradient(pattern));
+
+ Ok(true)
}
}
+#[derive(Clone, Default)]
+pub struct NodeRadialGradient {
+ pub common: RefCell<GradientCommon>,
+ pub variant: RefCell<GradientRadial>,
+}
+
impl NodeTrait for NodeRadialGradient {
fn set_atts(&self, _node: &RsvgNode, pbag: &PropertyBag<'_>) -> NodeResult {
- let mut g = self.gradient.borrow_mut();
+ let mut common = self.common.borrow_mut();
let mut cx = None;
let mut cy = None;
@@ -749,11 +610,11 @@ impl NodeTrait for NodeRadialGradient {
for (attr, value) in pbag.iter() {
match attr {
// Attributes common to linear and radial gradients
- local_name!("gradientUnits") => g.common.units = Some(attr.parse(value)?),
- local_name!("gradientTransform") => g.common.affine = Some(attr.parse(value)?),
- local_name!("spreadMethod") => g.common.spread = Some(attr.parse(value)?),
+ local_name!("gradientUnits") => common.units = Some(attr.parse(value)?),
+ local_name!("gradientTransform") => common.affine = Some(attr.parse(value)?),
+ local_name!("spreadMethod") => common.spread = Some(attr.parse(value)?),
local_name!("xlink:href") => {
- g.common.fallback = Some(Fragment::parse(value).attribute(attr)?)
+ common.fallback = Some(Fragment::parse(value).attribute(attr)?)
}
// Attributes specific to radial gradient
@@ -767,13 +628,61 @@ impl NodeTrait for NodeRadialGradient {
}
}
- g.variant = GradientVariant::Radial { cx, cy, r, fx, fy };
+ *self.variant.borrow_mut() = GradientRadial { cx, cy, r, fx, fy };
Ok(())
}
}
-impl_paint_source!(NodeRadialGradient);
+impl_resolve!(NodeRadialGradient);
+
+impl PaintSource for NodeRadialGradient {
+ type Source = NodeRadialGradient;
+
+ impl_paint_source_resolve!(
+ NodeRadialGradient,
+ NodeType::RadialGradient,
+ NodeLinearGradient,
+ NodeType::LinearGradient
+ );
+
+ fn set_pattern_on_draw_context(
+ &self,
+ gradient: &Self::Source,
+ values: &ComputedValues,
+ draw_ctx: &mut DrawingCtx,
+ opacity: &UnitInterval,
+ bbox: &BoundingBox,
+ ) -> Result<bool, RenderingError> {
+ assert!(gradient.is_resolved());
+
+ let units = gradient.common.borrow().units.unwrap();
+ let params = if units == GradientUnits(CoordUnits::ObjectBoundingBox) {
+ draw_ctx.push_view_box(1.0, 1.0)
+ } else {
+ draw_ctx.get_view_params()
+ };
+
+ let v = gradient.variant.borrow();
+ let n_cx = v.cx.as_ref().unwrap().normalize(values, ¶ms);
+ let n_cy = v.cy.as_ref().unwrap().normalize(values, ¶ms);
+ let n_r = v.r.as_ref().unwrap().normalize(values, ¶ms);
+ let n_fx = v.fx.as_ref().unwrap().normalize(values, ¶ms);
+ let n_fy = v.fy.as_ref().unwrap().normalize(values, ¶ms);
+
+ let (new_fx, new_fy) = fix_focus_point(n_fx, n_fy, n_cx, n_cy, n_r);
+ let mut pattern = cairo::RadialGradient::new(new_fx, new_fy, 0.0, n_cx, n_cy, n_r);
+
+ let cr = draw_ctx.get_cairo_context();
+ gradient
+ .common
+ .borrow_mut()
+ .set_on_pattern(&mut pattern, bbox, opacity);
+ cr.set_source(&cairo::Pattern::RadialGradient(pattern));
+
+ Ok(true)
+ }
+}
#[cfg(test)]
mod tests {
@@ -810,12 +719,12 @@ mod tests {
#[test]
fn gradient_resolved_from_defaults_is_really_resolved() {
- let mut gradient = Gradient {
- common: GradientCommon::unresolved(),
- variant: GradientVariant::unresolved_linear(),
- };
+ let mut l = NodeLinearGradient::default();
+ l.resolve_from_defaults();
+ assert!(l.is_resolved());
- gradient.resolve_from_defaults();
- assert!(gradient.is_resolved());
+ let mut r = NodeRadialGradient::default();
+ r.resolve_from_defaults();
+ assert!(r.is_resolved());
}
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]