summaryrefslogtreecommitdiff
path: root/rsvg/src/surface_utils/srgb.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rsvg/src/surface_utils/srgb.rs')
-rw-r--r--rsvg/src/surface_utils/srgb.rs95
1 files changed, 95 insertions, 0 deletions
diff --git a/rsvg/src/surface_utils/srgb.rs b/rsvg/src/surface_utils/srgb.rs
new file mode 100644
index 00000000..c745d0b4
--- /dev/null
+++ b/rsvg/src/surface_utils/srgb.rs
@@ -0,0 +1,95 @@
+//! Utility functions for dealing with sRGB colors.
+//!
+//! The constant values in this module are taken from <http://www.color.org/chardata/rgb/srgb.xalter>
+
+use crate::rect::IRect;
+use crate::surface_utils::{
+ iterators::Pixels,
+ shared_surface::{ExclusiveImageSurface, SharedImageSurface, SurfaceType},
+ ImageSurfaceDataExt, Pixel,
+};
+
+// Include the linearization and unlinearization tables.
+include!(concat!(env!("OUT_DIR"), "/srgb-codegen.rs"));
+
+/// Converts an sRGB color value to a linear sRGB color value (undoes the gamma correction).
+#[inline]
+pub fn linearize(c: u8) -> u8 {
+ LINEARIZE[usize::from(c)]
+}
+
+/// Converts a linear sRGB color value to a normal sRGB color value (applies the gamma correction).
+#[inline]
+pub fn unlinearize(c: u8) -> u8 {
+ UNLINEARIZE[usize::from(c)]
+}
+
+/// Processing loop of `map_unpremultiplied_components`. Extracted (and public) for benchmarking.
+#[inline]
+pub fn map_unpremultiplied_components_loop<F: Fn(u8) -> u8>(
+ surface: &SharedImageSurface,
+ output_surface: &mut ExclusiveImageSurface,
+ bounds: IRect,
+ f: F,
+) {
+ output_surface.modify(&mut |data, stride| {
+ for (x, y, pixel) in Pixels::within(surface, bounds) {
+ if pixel.a > 0 {
+ let alpha = f64::from(pixel.a) / 255f64;
+
+ let compute = |x| {
+ let x = f64::from(x) / alpha; // Unpremultiply alpha.
+ let x = (x + 0.5) as u8; // Round to nearest u8.
+ let x = f(x);
+ let x = f64::from(x) * alpha; // Premultiply alpha again.
+ (x + 0.5) as u8
+ };
+
+ let output_pixel = Pixel {
+ r: compute(pixel.r),
+ g: compute(pixel.g),
+ b: compute(pixel.b),
+ a: pixel.a,
+ };
+
+ data.set_pixel(stride, output_pixel, x, y);
+ }
+ }
+ });
+}
+
+/// Applies the function to each pixel component after unpremultiplying.
+fn map_unpremultiplied_components<F: Fn(u8) -> u8>(
+ surface: &SharedImageSurface,
+ bounds: IRect,
+ f: F,
+ new_type: SurfaceType,
+) -> Result<SharedImageSurface, cairo::Error> {
+ let (width, height) = (surface.width(), surface.height());
+ let mut output_surface = ExclusiveImageSurface::new(width, height, new_type)?;
+ map_unpremultiplied_components_loop(surface, &mut output_surface, bounds, f);
+
+ output_surface.share()
+}
+
+/// Converts an sRGB surface to a linear sRGB surface (undoes the gamma correction).
+#[inline]
+pub fn linearize_surface(
+ surface: &SharedImageSurface,
+ bounds: IRect,
+) -> Result<SharedImageSurface, cairo::Error> {
+ assert_eq!(surface.surface_type(), SurfaceType::SRgb);
+
+ map_unpremultiplied_components(surface, bounds, linearize, SurfaceType::LinearRgb)
+}
+
+/// Converts a linear sRGB surface to a normal sRGB surface (applies the gamma correction).
+#[inline]
+pub fn unlinearize_surface(
+ surface: &SharedImageSurface,
+ bounds: IRect,
+) -> Result<SharedImageSurface, cairo::Error> {
+ assert_eq!(surface.surface_type(), SurfaceType::LinearRgb);
+
+ map_unpremultiplied_components(surface, bounds, unlinearize, SurfaceType::SRgb)
+}