pride_overlay/effects/
overlay.rs

1use crate::prelude::*;
2use image::GenericImageView;
3use image::{ImageBuffer, Rgba, RgbaImage, imageops::overlay};
4use imageproc::{drawing::draw_filled_rect_mut, rect::Rect};
5use resvg::{
6    tiny_skia::{Pixmap, Transform},
7    usvg::{self, Tree},
8};
9
10/// Effect that overlays a pride [Flag] onto an image.
11#[derive(Builder)]
12#[builder(
13    const,
14    builder_type(doc {
15        /// Builder for the [Overlay] effect.
16    }),
17    start_fn(vis = "pub(crate)", name = "_builder")
18)]
19pub struct Overlay<'a> {
20    #[builder(start_fn)]
21    flag: Flag<'a>,
22    #[builder(default = Opacity::HALF)]
23    opacity: Opacity,
24}
25
26impl<'a> Effect for Overlay<'a> {
27    fn apply(&self, image: &mut image::DynamicImage) {
28        let (width, height) = image.dimensions();
29        let flag_overlay = create_flag_overlay(&self.flag, width, height, &self.opacity);
30        overlay(image, &flag_overlay, 0, 0);
31    }
32}
33
34impl<'a> Overlay<'a> {
35    /// Create a new [Overlay] [Effect] with a [Flag].
36    pub const fn builder(flag: Flag<'a>) -> OverlayBuilder<'a> {
37        Self::_builder(flag)
38    }
39}
40
41pub(crate) fn create_flag_overlay<'a>(
42    flag: &Flag<'a>,
43    width: u32,
44    height: u32,
45    opacity: &Opacity,
46) -> RgbaImage {
47    let alpha = opacity.get_raw();
48
49    match flag.svg {
50        Some(svg) => overlay_svg(svg, width, height, alpha),
51        None => overlay_colours(flag.colours, width, height, alpha),
52    }
53}
54
55fn overlay_svg(
56    SvgAsset { data, mode, .. }: SvgAsset,
57    width: u32,
58    height: u32,
59    alpha: u8,
60) -> RgbaImage {
61    let tree = Tree::from_data(data, &usvg::Options::default()).unwrap();
62    let mut pixmap = Pixmap::new(width, height).unwrap();
63
64    let size = tree.size();
65    let scale_x = width as f32 / size.width();
66    let scale_y = height as f32 / size.height();
67
68    let transform = match mode {
69        SvgScaleMode::Contain => {
70            let scale = scale_x.min(scale_y);
71            Transform::from_scale(scale, scale).post_translate(
72                (width as f32 - size.width() * scale) / 2.0,
73                (height as f32 - size.height() * scale) / 2.0,
74            )
75        }
76        SvgScaleMode::Cover => {
77            let scale = scale_x.max(scale_y);
78            Transform::from_scale(scale, scale).post_translate(
79                (width as f32 - size.width() * scale) / 2.0,
80                (height as f32 - size.height() * scale) / 2.0,
81            )
82        }
83        SvgScaleMode::Stretch => Transform::from_scale(scale_x, scale_y),
84        SvgScaleMode::None => Transform::identity(),
85    };
86
87    resvg::render(&tree, transform, &mut pixmap.as_mut());
88
89    let mut flag: RgbaImage = ImageBuffer::from_raw(width, height, pixmap.data().to_vec()).unwrap();
90    flag.pixels_mut().for_each(|pixel| pixel[3] = alpha);
91    flag
92}
93
94fn overlay_colours(colours: &[Colour], width: u32, height: u32, alpha: u8) -> RgbaImage {
95    let mut flag_image = RgbaImage::new(width, height);
96    let count = colours.len() as u32;
97
98    for (i, &Colour(r, g, b)) in colours.iter().enumerate() {
99        let i = i as u32;
100        let y_start = (i * height) / count;
101        let y_end = ((i + 1) * height) / count;
102
103        if y_end > y_start {
104            let rect = Rect::at(0, y_start as i32).of_size(width, y_end - y_start);
105            let colour = Rgba([r, g, b, alpha]);
106            draw_filled_rect_mut(&mut flag_image, rect, colour);
107        }
108    }
109
110    flag_image
111}