pride_overlay/
lib.rs

1mod flags;
2mod opacity;
3
4pub use flags::Flags;
5use image::{imageops, DynamicImage, ImageBuffer, Rgba};
6use imageproc::{
7    drawing::{draw_filled_circle, draw_filled_rect},
8    rect::Rect,
9};
10pub use opacity::Opacity;
11use std::ops::Div;
12
13type Image = ImageBuffer<Rgba<u8>, Vec<u8>>;
14
15fn draw<T>(
16    image: &DynamicImage,
17    flag: &[(u8, u8, u8)],
18    opacity: Option<Opacity>,
19    flag_transform: Option<Box<T>>,
20) -> Image
21where
22    T: Sized + Fn(Image, u32, u32) -> Image,
23{
24    // get image data
25    let mut image = image.to_rgba8();
26    let (width, height) = image.dimensions();
27
28    // draw the pride flag
29    let mut flag_image = ImageBuffer::<Rgba<u8>, Vec<u8>>::new(width, height);
30    let segment_count = flag.len();
31    let segment_size = (height as f32).div(segment_count as f32).ceil() as u32;
32    let a = opacity.unwrap_or(Opacity(127)).0;
33
34    for (i, (r, g, b)) in flag.iter().enumerate() {
35        let i = i as u32;
36        let rect = Rect::at(0, (i * segment_size) as i32).of_size(width, segment_size);
37        let colour = Rgba([*r, *g, *b, a]);
38
39        flag_image = draw_filled_rect(&flag_image, rect, colour);
40    }
41
42    // transform the flag if a transform closure has been provided
43    if let Some(transform) = flag_transform {
44        flag_image = transform(flag_image, width, height);
45    }
46
47    // overlay the flag image
48    imageops::overlay(&mut image, &flag_image, 0, 0);
49
50    image.clone()
51}
52
53/// Overlay a pride flag over an image
54pub fn overlay<T>(image: &DynamicImage, flag: T, opacity: Option<Opacity>) -> Image
55where
56    T: Into<Vec<(u8, u8, u8)>>,
57{
58    draw::<fn(Image, u32, u32) -> Image>(image, &flag.into(), opacity, None)
59}
60
61/// Overlay a pride flag ring over an image
62pub fn circle<T>(image: &DynamicImage, flag: T, thickness: Option<u8>) -> Image
63where
64    T: Into<Vec<(u8, u8, u8)>>,
65{
66    draw(
67        image,
68        &flag.into(),
69        Some(Opacity(255)),
70        Some(Box::new(move |img, width, height| {
71            draw_filled_circle(
72                &img,
73                ((width / 2) as i32, (height / 2) as i32),
74                (width / 2
75                    - if let Some(mut thickness) = thickness {
76                        if thickness > 10 {
77                            thickness = 10;
78                        }
79
80                        thickness * 8
81                    } else {
82                        24
83                    } as u32) as i32,
84                Rgba([0, 0, 0, 0]),
85            )
86        })),
87    )
88}