pconvert_rust/blending/
mod.rs

1//! Blending algorithms and associated utility functions and enums.
2
3pub mod algorithms;
4pub mod params;
5
6use algorithms::{
7    blend_alpha, blend_destination_over, blend_disjoint_debug, blend_disjoint_over,
8    blend_disjoint_under, blend_first_bottom, blend_first_top, blend_mask_top,
9    blend_multiplicative, blend_source_over,
10};
11use image::{ImageBuffer, Rgba};
12use params::BlendAlgorithmParams;
13use std::fmt;
14use std::fmt::{Display, Formatter};
15use std::result;
16use std::str::FromStr;
17
18/// Enumeration of supported blending modes.
19#[derive(Clone, Debug)]
20pub enum BlendAlgorithm {
21    Alpha,
22    Multiplicative,
23    SourceOver,
24    DestinationOver,
25    MaskTop,
26    FirstTop,
27    FirstBottom,
28    DisjointOver,
29    DisjointUnder,
30    DisjointDebug,
31}
32
33impl FromStr for BlendAlgorithm {
34    type Err = String;
35
36    fn from_str(s: &str) -> result::Result<Self, Self::Err> {
37        match s {
38            "alpha" => Ok(BlendAlgorithm::Alpha),
39            "multiplicative" => Ok(BlendAlgorithm::Multiplicative),
40            "source_over" => Ok(BlendAlgorithm::SourceOver),
41            "destination_over" => Ok(BlendAlgorithm::DestinationOver),
42            "mask_top" => Ok(BlendAlgorithm::MaskTop),
43            "first_top" => Ok(BlendAlgorithm::FirstTop),
44            "first_bottom" => Ok(BlendAlgorithm::FirstBottom),
45            "disjoint_over" => Ok(BlendAlgorithm::DisjointOver),
46            "disjoint_under" => Ok(BlendAlgorithm::DisjointUnder),
47            "disjoint_debug" => Ok(BlendAlgorithm::DisjointDebug),
48            s => Err(s.to_string()),
49        }
50    }
51}
52
53impl Display for BlendAlgorithm {
54    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
55        match self {
56            BlendAlgorithm::Alpha => write!(f, "alpha"),
57            BlendAlgorithm::Multiplicative => write!(f, "multiplicative"),
58            BlendAlgorithm::SourceOver => write!(f, "source_over"),
59            BlendAlgorithm::DestinationOver => write!(f, "destination_over"),
60            BlendAlgorithm::MaskTop => write!(f, "mask_top"),
61            BlendAlgorithm::FirstTop => write!(f, "first_top"),
62            BlendAlgorithm::FirstBottom => write!(f, "first_bottom"),
63            BlendAlgorithm::DisjointOver => write!(f, "disjoint_over"),
64            BlendAlgorithm::DisjointUnder => write!(f, "disjoint_under"),
65            BlendAlgorithm::DisjointDebug => write!(f, "disjoint_debug"),
66        }
67    }
68}
69
70/// Blends two images buffers with the given blending function and
71/// optional parameters.
72///
73/// # Arguments
74///
75/// * `bot` - An image buffer corresponding to the bottom layer, in typical
76/// composition language this should be considered the `source`.
77/// * `top` - An image buffer corresponding to the top layer, in typical
78/// composition language this should be considered the `destination`.
79/// * `blending_algorithm` - A function that blends two pixels according
80/// to optional blending parameters.
81/// * `algorithm_params` - A optional map of key-value pairs of blending
82/// properties and values.
83///
84/// # Examples
85///
86/// ```no_run
87/// use pconvert_rust::blending::{blend_images, get_blending_algorithm, BlendAlgorithm};
88/// use pconvert_rust::utils::read_png_from_file;
89///
90/// let mut bot = read_png_from_file("bot.png".to_string(), false).unwrap();
91/// let top = read_png_from_file("top.png".to_string(), false).unwrap();
92/// let algorithm_fn = get_blending_algorithm(&BlendAlgorithm::Alpha);
93///
94/// blend_images(&mut bot, &top, &algorithm_fn, &None);
95/// ```
96pub fn blend_images(
97    bot: &mut ImageBuffer<Rgba<u8>, Vec<u8>>,
98    top: &ImageBuffer<Rgba<u8>, Vec<u8>>,
99    blending_algorithm: &impl Fn((&mut Rgba<u8>, &Rgba<u8>), &Option<BlendAlgorithmParams>),
100    algorithm_params: &Option<BlendAlgorithmParams>,
101) {
102    for pixel_pair in bot.pixels_mut().zip(top.pixels()) {
103        blending_algorithm(pixel_pair, algorithm_params);
104    }
105}
106
107/// Demultiplies an image buffer, by applying the demultiply operation over the
108/// complete set of pixels in the provided image buffer.
109///
110/// # Arguments
111///
112/// * `img` - The image buffer to demultiply.
113pub fn demultiply_image(img: &mut ImageBuffer<Rgba<u8>, Vec<u8>>) {
114    for pixel in img.pixels_mut() {
115        demultiply_pixel(pixel);
116    }
117}
118
119/// Multiplies an image buffer, running the opposite operation over the
120/// complete set of pixels in the image buffer.
121///
122/// # Arguments
123///
124/// * `img` - The image buffer to multiply.
125pub fn multiply_image(img: &mut ImageBuffer<Rgba<u8>, Vec<u8>>) {
126    for pixel in img.pixels_mut() {
127        multiply_pixel(pixel);
128    }
129}
130
131/// Matches a `BlendAlgorithm` enum variant with a blend function.
132///
133/// # Arguments
134///
135/// * `algorithm` - The BlendAlgorithm enum variant.
136pub fn get_blending_algorithm(
137    algorithm: &BlendAlgorithm,
138) -> impl Fn((&mut Rgba<u8>, &Rgba<u8>), &Option<BlendAlgorithmParams>) {
139    match algorithm {
140        BlendAlgorithm::Alpha => blend_alpha,
141        BlendAlgorithm::Multiplicative => blend_multiplicative,
142        BlendAlgorithm::SourceOver => blend_source_over,
143        BlendAlgorithm::DestinationOver => blend_destination_over,
144        BlendAlgorithm::MaskTop => blend_mask_top,
145        BlendAlgorithm::FirstTop => blend_first_top,
146        BlendAlgorithm::FirstBottom => blend_first_bottom,
147        BlendAlgorithm::DisjointOver => blend_disjoint_over,
148        BlendAlgorithm::DisjointUnder => blend_disjoint_under,
149        BlendAlgorithm::DisjointDebug => blend_disjoint_debug,
150    }
151}
152
153/// Returns whether or not a `BlendAlgorithm` enum variant corresponds to a
154/// multiplied blending algorithm.
155///
156/// # Arguments
157///
158/// * `algorithm` - The BlendAlgorithm enum variant.
159pub fn is_algorithm_multiplied(algorithm: &BlendAlgorithm) -> bool {
160    match algorithm {
161        BlendAlgorithm::Alpha => false,
162        BlendAlgorithm::Multiplicative => false,
163        BlendAlgorithm::SourceOver => false,
164        BlendAlgorithm::DestinationOver => false,
165        BlendAlgorithm::MaskTop => false,
166        BlendAlgorithm::FirstTop => false,
167        BlendAlgorithm::FirstBottom => false,
168        BlendAlgorithm::DisjointOver => true,
169        BlendAlgorithm::DisjointUnder => true,
170        BlendAlgorithm::DisjointDebug => true,
171    }
172}
173
174fn demultiply_pixel(pixel: &mut Rgba<u8>) {
175    let (r, g, b, a) = (pixel[0], pixel[1], pixel[2], pixel[3]);
176    let af = a as f32 / 255.0;
177
178    let r = (r as f32 * af).round() as u8;
179    let g = (g as f32 * af).round() as u8;
180    let b = (b as f32 * af).round() as u8;
181
182    pixel[0] = r;
183    pixel[1] = g;
184    pixel[2] = b;
185}
186
187fn multiply_pixel(pixel: &mut Rgba<u8>) {
188    let (r, g, b, a) = (pixel[0], pixel[1], pixel[2], pixel[3]);
189    let af = a as f32 / 255.0;
190
191    let r = (r as f32 / af).round() as u8;
192    let g = (g as f32 / af).round() as u8;
193    let b = (b as f32 / af).round() as u8;
194
195    pixel[0] = r;
196    pixel[1] = g;
197    pixel[2] = b;
198}