[librsvg: 3/4] gitlab#328 - Make masking work on big-endian



commit 242842c01849b7ecb950902c6491c7994048d091
Author: Federico Mena Quintero <federico gnome org>
Date:   Thu Aug 30 10:56:50 2018 -0500

    gitlab#328 - Make masking work on big-endian
    
    The code only worked in little-endian machines; it wrote to the pixels
    directly.  Now it uses the pixel iterator for SharedImageSurface,
    which handles endianness properly.
    
    https://gitlab.gnome.org/GNOME/librsvg/issues/328

 rsvg_internals/src/mask.rs | 98 +++++++++++++++++++++++++++++++---------------
 1 file changed, 66 insertions(+), 32 deletions(-)
---
diff --git a/rsvg_internals/src/mask.rs b/rsvg_internals/src/mask.rs
index b64b1198..83fd158f 100644
--- a/rsvg_internals/src/mask.rs
+++ b/rsvg_internals/src/mask.rs
@@ -7,12 +7,20 @@ use attributes::Attribute;
 use coord_units::CoordUnits;
 use drawing_ctx::DrawingCtx;
 use error::RenderingError;
+use filters::context::IRect;
 use handle::RsvgHandle;
 use length::{Length, LengthDir};
 use node::{NodeResult, NodeTrait, RsvgNode};
 use parsers::{parse, parse_and_validate, Parse};
 use property_bag::PropertyBag;
 use state::Opacity;
+use surface_utils::{
+    iterators::Pixels,
+    shared_surface::SharedImageSurface,
+    shared_surface::SurfaceType,
+    ImageSurfaceDataExt,
+    Pixel,
+};
 
 coord_units!(MaskUnits, CoordUnits::ObjectBoundingBox);
 coord_units!(MaskContentUnits, CoordUnits::UserSpaceOnUse);
@@ -166,46 +174,72 @@ impl NodeMask {
     }
 }
 
-// Takes a surface and overwrites the alpha channel of each pixel
-// with the luminance of that pixel's RGB values.
+// Returns a surface whose alpha channel for each pixel is equal to the
+// luminance of that pixel's unpremultiplied RGB values.  The resulting
+// surface's RGB values are not meanignful; only the alpha channel has
+// useful luminance data.
+//
+// This is to get a mask suitable for use with cairo_mask_surface().
 fn compute_luminance_to_alpha(
-    mut surface: cairo::ImageSurface,
+    surface: cairo::ImageSurface,
     opacity: u8,
 ) -> Result<cairo::ImageSurface, cairo::Status> {
-    let width = surface.get_width();
-    let rowstride = surface.get_stride() as usize;
+    let surface = SharedImageSurface::new(surface, SurfaceType::SRgb)?;
+
+    let width = surface.width();
+    let height = surface.height();
+
+    let bounds = IRect {
+        x0: 0,
+        y0: 0,
+        x1: width,
+        y1: height,
+    };
+
+    let opacity = opacity as u32;
+
+    let mut output = cairo::ImageSurface::create(cairo::Format::ARgb32, width, height)?;
+
+    let output_stride = output.get_stride() as usize;
 
     {
-        let mut pixels = surface.get_data().unwrap();
-
-        for row in pixels.chunks_mut(rowstride) {
-            for p in row[..4 * width as usize].chunks_mut(4) {
-                //  Assuming, the pixel is linear RGB (not sRGB)
-                //  y = luminance
-                //  Y = 0.2126 R + 0.7152 G + 0.0722 B
-                //  1.0 opacity = 255
-                //
-                //  When Y = 1.0, pixel for mask should be 0xFFFFFFFF
-                //    (you get 1.0 luminance from 255 from R, G and B)
-                //
-                // r_mult = 0xFFFFFFFF / (255.0 * 255.0) * .2126 = 14042.45  ~= 14042
-                // g_mult = 0xFFFFFFFF / (255.0 * 255.0) * .7152 = 47239.69  ~= 47240
-                // b_mult = 0xFFFFFFFF / (255.0 * 255.0) * .0722 =  4768.88  ~= 4769
-                //
-                // This allows for the following expected behaviour:
-                //    (we only care about the most sig byte)
-                // if pixel = 0x00FFFFFF, pixel' = 0xFF......
-                // if pixel = 0x00020202, pixel' = 0x02......
-                // if pixel = 0x00000000, pixel' = 0x00......
-                //
-                // NOTE: the following assumes little-endian
-                let (r, g, b, o) = (p[2] as u32, p[1] as u32, p[0] as u32, opacity as u32);
-                p[3] = (((r * 14042 + g * 47240 + b * 4769) * o) >> 24) as u8;
-            }
+        let mut output_data = output.get_data().unwrap();
+
+        for (x, y, pixel) in Pixels::new(&surface, bounds) {
+            //  Assuming, the pixel is linear RGB (not sRGB)
+            //  y = luminance
+            //  Y = 0.2126 R + 0.7152 G + 0.0722 B
+            //  1.0 opacity = 255
+            //
+            //  When Y = 1.0, pixel for mask should be 0xFFFFFFFF
+            //    (you get 1.0 luminance from 255 from R, G and B)
+            //
+            // r_mult = 0xFFFFFFFF / (255.0 * 255.0) * .2126 = 14042.45  ~= 14042
+            // g_mult = 0xFFFFFFFF / (255.0 * 255.0) * .7152 = 47239.69  ~= 47240
+            // b_mult = 0xFFFFFFFF / (255.0 * 255.0) * .0722 =  4768.88  ~= 4769
+            //
+            // This allows for the following expected behaviour:
+            //    (we only care about the most sig byte)
+            // if pixel = 0x00FFFFFF, pixel' = 0xFF......
+            // if pixel = 0x00020202, pixel' = 0x02......
+            // if pixel = 0x00000000, pixel' = 0x00......
+
+            let r = pixel.r as u32;
+            let g = pixel.g as u32;
+            let b = pixel.b as u32;
+
+            let output_pixel = Pixel {
+                r: 0,
+                g: 0,
+                b: 0,
+                a: (((r * 14042 + g * 47240 + b * 4769) * opacity) >> 24) as u8,
+            };
+
+            output_data.set_pixel(output_stride, output_pixel, x, y);
         }
     }
 
-    Ok(surface)
+    Ok(output)
 }
 
 impl NodeTrait for NodeMask {


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