1use super::IDrawingContextImpl;
2use crate::media::imaging::BitmapBlendingMode;
3use crate::media::{ColorMatrix, ColorMatrixAlphaMode};
4use crate::{PixelSize, Rect};
5use std::sync::Arc;
6
7pub trait IDrawingContextImplWithLayerEffects: IDrawingContextImpl {
15 fn push_blend_layer(&mut self, bounds: Rect, mode: BitmapBlendingMode);
21
22 fn pop_blend_layer(&mut self);
24
25 fn push_color_matrix_layer(&mut self, bounds: Rect, matrix: &ColorMatrix, alpha_mode: ColorMatrixAlphaMode);
32
33 fn pop_color_matrix_layer(&mut self);
35
36 fn push_multiply_layer(&mut self, bounds: Rect, factors: &MultiplyLayerFactors);
46
47 fn pop_multiply_layer(&mut self);
49}
50
51#[derive(Clone, Debug, PartialEq)]
55pub struct MultiplyLayerFactors {
56 pub size: PixelSize,
58 pub rgba: Arc<Vec<u8>>,
60 pub scale: f32,
62}
63
64impl MultiplyLayerFactors {
65 pub fn factor_at(&self, u: f64, v: f64) -> [f32; 3] {
69 let (width, height) = (self.size.width.max(1) as usize, self.size.height.max(1) as usize);
70 if self.rgba.len() < width * height * 4 {
71 return [1.0; 3];
72 }
73 let x = (u * width as f64 - 0.5).clamp(0.0, (width - 1) as f64);
74 let y = (v * height as f64 - 0.5).clamp(0.0, (height - 1) as f64);
75 let (x0, y0) = (x.floor() as usize, y.floor() as usize);
76 let (x1, y1) = ((x0 + 1).min(width - 1), (y0 + 1).min(height - 1));
77 let (fx, fy) = ((x - x0 as f64) as f32, (y - y0 as f64) as f32);
78 let sample = |x: usize, y: usize, channel: usize| f32::from(self.rgba[(y * width + x) * 4 + channel]) / 255.0;
79 let mut factors = [0.0; 3];
80 for (channel, factor) in factors.iter_mut().enumerate() {
81 let top = sample(x0, y0, channel) * (1.0 - fx) + sample(x1, y0, channel) * fx;
82 let bottom = sample(x0, y1, channel) * (1.0 - fx) + sample(x1, y1, channel) * fx;
83 *factor = (top * (1.0 - fy) + bottom * fy) * self.scale;
84 }
85 factors
86 }
87
88 pub fn apply_to_premultiplied_rgba8(&self, rgba: &mut [u8], width: usize, to_unit: impl Fn(f64, f64) -> Option<(f64, f64)>) {
93 if width == 0 {
94 return;
95 }
96 for (index, pixel) in rgba.chunks_exact_mut(4).enumerate() {
97 if pixel[3] == 0 {
98 continue;
99 }
100 let (x, y) = ((index % width) as f64 + 0.5, (index / width) as f64 + 0.5);
101 let Some((u, v)) = to_unit(x, y) else {
102 pixel.copy_from_slice(&[0, 0, 0, 0]);
103 continue;
104 };
105 let factors = self.factor_at(u, v);
106 let alpha = f32::from(pixel[3]);
107 for channel in 0..3 {
108 pixel[channel] = (f32::from(pixel[channel]) * factors[channel]).round().clamp(0.0, alpha) as u8;
109 }
110 }
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117
118 fn factors(rgba: Vec<u8>, width: i32, height: i32, scale: f32) -> MultiplyLayerFactors {
119 MultiplyLayerFactors { size: PixelSize::new(width, height), rgba: Arc::new(rgba), scale }
120 }
121
122 #[test]
123 fn the_factors_are_sampled_between_the_centres_of_the_samples() {
124 let map = factors(vec![0, 0, 0, 255, 255, 255, 255, 255], 2, 1, 1.0);
125 assert_eq!([0.0; 3], map.factor_at(0.0, 0.5));
126 assert_eq!([1.0; 3], map.factor_at(1.0, 0.5));
127 let middle = map.factor_at(0.5, 0.5);
128 assert!((middle[0] - 0.5).abs() < 1e-6);
129 }
130
131 #[test]
132 fn a_scale_brightens_and_the_result_is_clamped_to_the_alpha() {
133 let map = factors(vec![255, 128, 0, 255], 1, 1, 2.0);
134 let mut pixels = vec![100, 100, 100, 200, 50, 50, 50, 50];
135 map.apply_to_premultiplied_rgba8(&mut pixels, 2, |x, y| Some((x / 2.0, y)));
136 assert_eq!(vec![200, 100, 0, 200, 50, 50, 0, 50], pixels);
137 }
138
139 #[test]
140 fn pixels_outside_the_factors_are_cleared() {
141 let map = factors(vec![255, 255, 255, 255], 1, 1, 1.0);
142 let mut pixels = vec![10, 10, 10, 10];
143 map.apply_to_premultiplied_rgba8(&mut pixels, 1, |_, _| None);
144 assert_eq!(vec![0, 0, 0, 0], pixels);
145 }
146}