[librsvg/rustification] gradient.rs: Implementation of gradient fallbacks



commit 0e9bda14796ed6aa862a324a27b01ce1fdc8f885
Author: Federico Mena Quintero <federico gnome org>
Date:   Fri Nov 25 13:45:01 2016 -0600

    gradient.rs: Implementation of gradient fallbacks

 rust/src/gradient.rs |  242 ++++++++++++++++++++++++++++++++++++++------------
 rust/src/length.rs   |    6 +-
 2 files changed, 188 insertions(+), 60 deletions(-)
---
diff --git a/rust/src/gradient.rs b/rust/src/gradient.rs
index af0ef73..a68f21d 100644
--- a/rust/src/gradient.rs
+++ b/rust/src/gradient.rs
@@ -5,48 +5,67 @@ use length::*;
 
 use self::cairo::MatrixTrait;
 
-struct ColorStop {
-    offset: f64,
-    rgba:   u32
+#[derive(Copy, Clone)]
+pub struct ColorStop {
+    pub offset: f64,
+    pub rgba:   u32
 }
 
 /* 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>.
  */
-struct GradientCommon {
-    obj_bbox: Option<bool>,
-    affine:   Option<cairo::Matrix>,
-    spread:   Option<cairo::enums::Extend>,
-    fallback: Option<String>,
-    stops:    Option<Vec<ColorStop>>
+pub struct GradientCommon {
+    pub obj_bbox: Option<bool>,
+    pub affine:   Option<cairo::Matrix>,
+    pub spread:   Option<cairo::enums::Extend>,
+    pub fallback: Option<String>,
+    pub stops:    Option<Vec<ColorStop>>
 }
 
-struct LinearGradient {
-    common: GradientCommon,
-    x1:     Option<RsvgLength>,
-    y1:     Option<RsvgLength>,
-    x2:     Option<RsvgLength>,
-    y2:     Option<RsvgLength>
+#[derive(Copy, Clone)]
+pub enum GradientVariant {
+    Linear {
+        x1: Option<RsvgLength>,
+        y1: Option<RsvgLength>,
+        x2: Option<RsvgLength>,
+        y2: Option<RsvgLength>
+    },
+
+    Radial {
+        cx: Option<RsvgLength>,
+        cy: Option<RsvgLength>,
+        r:  Option<RsvgLength>,
+        fx: Option<RsvgLength>,
+        fy: Option<RsvgLength>,
+    }
 }
 
-struct RadialGradient {
-    common: GradientCommon,
-    cx:     Option<RsvgLength>,
-    cy:     Option<RsvgLength>,
-    r:      Option<RsvgLength>,
-    fx:     Option<RsvgLength>,
-    fy:     Option<RsvgLength>,
+pub struct Gradient {
+    pub common: GradientCommon,
+    pub variant: GradientVariant
 }
 
 impl GradientCommon {
-    fn get_defaults () -> GradientCommon {
+    fn new (obj_bbox: Option<bool>,
+            affine:   Option<cairo::Matrix>,
+            spread:   Option<cairo::enums::Extend>,
+            fallback: Option<String>,
+            stops:    Option<Vec<ColorStop>>) -> GradientCommon {
         GradientCommon {
-            obj_bbox: Some (true),                         // these are per the spec
-            affine:   Some (cairo::Matrix::identity ()),
-            spread:   Some (cairo::enums::Extend::Pad),
-            fallback: None,
-            stops:    Some (Vec::<ColorStop>::new ())        // empty array of color stops
+            obj_bbox: obj_bbox,
+            affine:   affine,
+            spread:   spread,
+            fallback: fallback,
+            stops:    stops
+        }
+    }
+
+    fn clone_stops (&self) -> Option<Vec<ColorStop>> {
+        if let Some (ref stops) = self.stops {
+            Some (stops.clone ())
+        } else {
+            None
         }
     }
 
@@ -56,46 +75,155 @@ impl GradientCommon {
             self.spread.is_some () &&
             self.stops.is_some ()
     }
-}
 
-impl LinearGradient {
-    fn get_defaults () -> LinearGradient {
-        LinearGradient {
-            common: GradientCommon::get_defaults (),
-            x1:     Some (RsvgLength::parse ("0%", LengthDir::Horizontal)),  // these are per the spec
-            y1:     Some (RsvgLength::parse ("0%", LengthDir::Vertical)),
-            x2:     Some (RsvgLength::parse ("100%", LengthDir::Horizontal)),
-            y2:     Some (RsvgLength::parse ("0%", LengthDir::Vertical))
+    fn resolve_from_defaults (&mut self) {
+        /* These are per the spec */
+
+        if self.obj_bbox.is_none () { self.obj_bbox = Some (true); }
+        if self.affine.is_none ()   { self.affine   = Some (cairo::Matrix::identity ()); }
+        if self.spread.is_none ()   { self.spread   = Some (cairo::enums::Extend::Pad); }
+
+        self.fallback = None;
+
+        if self.stops.is_none ()    { self.stops    = Some (Vec::<ColorStop>::new ()); } // empty array of 
color stops
+    }
+
+    fn resolve_from_fallback (&mut self, fallback: &GradientCommon) {
+        if self.obj_bbox.is_none () { self.obj_bbox = fallback.obj_bbox; }
+        if self.affine.is_none ()   { self.affine   = fallback.affine; }
+        if self.spread.is_none ()   { self.spread   = fallback.spread; }
+        if self.stops.is_none ()    { self.stops    = fallback.clone_stops (); }
+
+        if self.fallback.is_none () {
+            self.fallback = clone_fallback_name (&fallback.fallback);
         }
     }
+}
+
+fn clone_fallback_name (fallback: &Option<String>) -> Option<String> {
+    if let Some (ref fallback_name) = *fallback {
+        Some (fallback_name.clone ())
+    } else {
+        None
+    }
+}
 
+impl GradientVariant {
     fn is_resolved (&self) -> bool {
-        self.common.is_resolved () &&
-            self.x1.is_some () &&
-            self.y1.is_some () &&
-            self.x2.is_some () &&
-            self.y2.is_some ()
+        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 resolve_from_defaults (&mut self) {
+        /* These are per the spec */
+
+        match *self {
+            GradientVariant::Linear { ref mut x1, ref mut y1, ref mut x2, ref mut y2 } => {
+                if x1.is_none () { *x1 = Some (RsvgLength::parse ("0%", LengthDir::Horizontal)); }
+                if y1.is_none () { *y1 = Some (RsvgLength::parse ("0%", LengthDir::Vertical)); }
+                if x2.is_none () { *x2 = Some (RsvgLength::parse ("100%", LengthDir::Horizontal)); }
+                if y2.is_none () { *y2 = Some (RsvgLength::parse ("0%", LengthDir::Vertical)); }
+            },
+
+            GradientVariant::Radial { ref mut cx, ref mut cy, ref mut r, ref mut fx, ref mut fy } => {
+                if cx.is_none () { *cx = Some (RsvgLength::parse ("50%", LengthDir::Horizontal)); }
+                if cy.is_none () { *cy = Some (RsvgLength::parse ("50%", LengthDir::Vertical)); }
+                if r.is_none ()  { *r  = Some (RsvgLength::parse ("50%", LengthDir::Both)); }
+
+                /* fx and fy fall back to the presentational value of cx and cy */
+                if fx.is_none () { *fx = *cx }
+                if fy.is_none () { *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 {
+                    if x1.is_none () { *x1 = x1f; }
+                    if y1.is_none () { *y1 = y1f; }
+                    if x2.is_none () { *x2 = x2f; }
+                    if y2.is_none () { *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 {
+                    if cx.is_none () { *cx = cxf; }
+                    if cy.is_none () { *cy = cyf; }
+                    if r.is_none ()  { *r  = rf;  }
+                    if fx.is_none () { *fx = fxf; }
+                    if fy.is_none () { *fy = fyf; }
+                }
+            }
+        }
     }
 }
 
-impl RadialGradient {
-    fn get_defaults () -> RadialGradient {
-        RadialGradient {
-            common: GradientCommon::get_defaults (),
-            cx:     Some (RsvgLength::parse ("50%", LengthDir::Horizontal)),
-            cy:     Some (RsvgLength::parse ("50%", LengthDir::Vertical)),
-            r:      Some (RsvgLength::parse ("50%", LengthDir::Both)),
-            fx:     Some (RsvgLength::parse ("50%", LengthDir::Horizontal)), // per the spec, equal to cx
-            fy:     Some (RsvgLength::parse ("50%", LengthDir::Vertical))    // per the spec, equal to cy
+impl Gradient {
+    fn new (common: GradientCommon, variant: GradientVariant) -> Gradient {
+        Gradient {
+            common: common,
+            variant: variant
         }
     }
 
     fn is_resolved (&self) -> bool {
-        self.common.is_resolved () &&
-            self.cx.is_some () && 
-            self.cy.is_some () && 
-            self.r.is_some () && 
-            self.fx.is_some () && 
-            self.fy.is_some ()
+        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);
+    }
+}
+
+trait FallbackSource {
+    fn get_fallback (&self, name: &str) -> Option<Gradient>;
+}
+
+fn resolve_gradient (gradient: &Gradient, fallback_source: &FallbackSource) -> Gradient {
+    let mut result = Gradient::new (GradientCommon::new (gradient.common.obj_bbox,
+                                                         gradient.common.affine,
+                                                         gradient.common.spread,
+                                                         clone_fallback_name (&gradient.common.fallback),
+                                                         gradient.common.clone_stops ()),
+                                    gradient.variant);
+
+    while !result.is_resolved () {
+        let mut opt_fallback: Option<Gradient> = None;
+
+        if let Some (ref fallback_name) = result.common.fallback {
+            opt_fallback = fallback_source.get_fallback (&**fallback_name);
+        }
+
+        if let Some (fallback_gradient) = opt_fallback {
+            result.resolve_from_fallback (&fallback_gradient);
+        } else {
+            result.resolve_from_defaults ();
+            break;
+        }
+    }
+
+    result
 }
diff --git a/rust/src/length.rs b/rust/src/length.rs
index 16d0f05..7060e3b 100644
--- a/rust/src/length.rs
+++ b/rust/src/length.rs
@@ -11,7 +11,7 @@ use drawing_ctx::RsvgDrawingCtx;
 
 /* Keep this in sync with ../../rsvg-private.h:LengthUnit */
 #[repr(C)]
-#[derive(Debug, PartialEq)]
+#[derive(Debug, PartialEq, Copy, Clone)]
 pub enum LengthUnit {
     Default,
     Percent,
@@ -24,7 +24,7 @@ pub enum LengthUnit {
 
 /* Keep this in sync with ../../rsvg-private.h:LengthDir */
 #[repr(C)]
-#[derive(Debug, PartialEq)]
+#[derive(Debug, PartialEq, Copy, Clone)]
 pub enum LengthDir {
     Horizontal,
     Vertical,
@@ -37,7 +37,7 @@ pub enum LengthDir {
  */
 /* Keep this in sync with ../../rsvg-private.h:RsvgLength */
 #[repr(C)]
-#[derive(Debug, PartialEq)]
+#[derive(Debug, PartialEq, Copy, Clone)]
 pub struct RsvgLength {
     length: f64,
     unit: LengthUnit,


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]