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