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.

GNOME/librsvg#328
parent 957d32d0
......@@ -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 {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment