1#![allow(non_snake_case)]
2
3use num_traits::Float;
4use {
5 std::{sync::Arc, ops::Fn},
6 euclid::{Point2D, Rect, Size2D, Box2D},
7 image::{
8 DynamicImage, GenericImageView, Pixel, Rgba, RgbaImage,
9 imageops::FilterType
10 },
11 num_traits::{NumCast, AsPrimitive},
12 crate::{
13 drawing::{Draw, Shape, Texture, rescale_bounding_box},
14 geometry::{BoundingBox, PixelSpace, WorldSpace},
15 sdf::SDF
16 }
17};
18
19impl<Ty, P> SDF<P> for Ty where Ty: AsRef<dyn Draw<P, RgbaImage>> { fn sdf(&self, pixel: Point2D<P, WorldSpace>) -> P { self.as_ref().sdf(pixel) } }
20impl<Ty, P> BoundingBox<P> for Ty where Ty: AsRef<dyn Draw<P, RgbaImage>> { fn bounding_box(&self) -> Box2D<P, WorldSpace> { self.as_ref().bounding_box() } }
21
22impl <Cutie, P: Float> Draw<P, RgbaImage> for Texture<Cutie, Rgba<u8>>
23 where Cutie: Shape<P> + Clone,
24 P: NumCast + AsPrimitive<f64>
25{
26 fn draw(&self, image: &mut RgbaImage) {
27 self.shape.clone()
28 .texture(|_| self.texture)
29 .draw(image);
30 }
31}
32
33impl <'a, Cutie, P> Draw<P, RgbaImage> for Texture<Cutie, &'a DynamicImage>
34 where Cutie: Shape<P>,
35 P: Float + AsPrimitive<f64>
36{
37 fn draw(&self, image: &mut RgbaImage) {
38 let resolution: Size2D<_, PixelSpace> = image.dimensions().into();
39 let (bounding_box, offset, min_side) =
40 rescale_bounding_box(self.shape.bounding_box().to_f64(), resolution);
41 let bounding_box = match bounding_box {
42 Some(x) => x,
43 None => return
44 };
45 let Δp = 1.0 / min_side;
46 let tex = rescale_texture(self.texture, bounding_box.size().to_u32());
47
48 itertools::iproduct!(bounding_box.y_range(), bounding_box.x_range())
49 .map(|(y, x)| Point2D::<_, PixelSpace>::from([x, y]))
50 .for_each(|pixel| {
51 let pixel_world = ((pixel.to_f64() - offset).to_vector() / min_side)
52 .cast_unit().to_point();
53 let tex_px = pixel - bounding_box.min.to_vector();
54 let tex_px = tex.get_pixel(tex_px.x, tex_px.y);
55
56 let sdf = self.sdf(pixel_world.cast::<P>()).as_();
57 let pixel = image.get_pixel_mut(pixel.x, pixel.y);
58 *pixel = sdf_overlay_aa(sdf, Δp, *pixel, tex_px);
59 });
60 }
61}
62
63impl <Cutie, F, P> Draw<P, RgbaImage> for Texture<Cutie, F>
66 where Cutie: Shape<P>,
67 F: Fn(Point2D<P, WorldSpace>) -> Rgba<u8>,
68 P: Float + AsPrimitive<f64>
69{
70 fn draw(&self, image: &mut RgbaImage) {
71 let resolution: Size2D<_, PixelSpace> = image.dimensions().into();
72 let (bounding_box, offset, min_side) =
73 rescale_bounding_box(self.bounding_box().to_f64(), resolution);
74 let bounding_box = match bounding_box {
75 Some(x) => x,
76 None => return };
78 let Δp = 1.0 / min_side;
79 let tex_scale = bounding_box.size().width.min(bounding_box.size().height) as f64;
80
81 itertools::iproduct!(bounding_box.y_range(), bounding_box.x_range())
82 .map(|(y, x)| Point2D::<_, PixelSpace>::from([x, y]))
83 .for_each(|pixel| {
84 let pixel_world = ((pixel.to_f64() - offset).to_vector() / min_side)
85 .cast_unit().to_point();
86 let sdf = self.sdf(pixel_world.cast::<P>()).as_();
87
88 let tex_px = ((pixel - bounding_box.min.to_vector()).to_f64() / tex_scale).cast_unit();
89 let tex_px = (self.texture)(tex_px.cast::<P>());
90
91 let pixel = image.get_pixel_mut(pixel.x, pixel.y);
92 *pixel = sdf_overlay_aa(sdf, Δp, *pixel, tex_px);
93 });
94 }
95}
96
97impl <Cutie, P> Draw<P, RgbaImage> for Texture<Cutie, DynamicImage>
98 where Cutie: Shape<P> + Clone,
99 P: Float + AsPrimitive<f64>
100{
101 fn draw(&self, image: &mut RgbaImage) {
102 Texture {
103 shape: self.shape.clone(),
104 texture: &self.texture
105 }.draw(image)
106 }
107}
108
109impl <Cutie, P> Draw<P, RgbaImage> for Texture<Cutie, Arc<DynamicImage>>
110 where Cutie: Shape<P> + Clone,
111 P: Float + AsPrimitive<f64>
112{
113 fn draw(&self, image: &mut RgbaImage) {
114 Texture {
115 shape: self.shape.clone(),
116 texture: self.texture.as_ref()
117 }.draw(image)
118 }
119}
120
121fn rescale_texture(texture: &DynamicImage, size: Size2D<u32, PixelSpace>) -> DynamicImage {
124 let tex_size = Size2D::from(texture.dimensions()).to_f32();
125 let scaling_factor = tex_size.to_vector()
126 .component_div(size.to_f32().to_vector());
127 let scaling_factor = scaling_factor.x.min(scaling_factor.y);
128 let bound_inner = size.to_f32() * scaling_factor;
129 let bound_inner = Rect::new(
130 ((tex_size - bound_inner) / 2.0).to_vector().to_point(),
131 bound_inner
132 ).to_u32();
133 texture.crop_imm(
134 bound_inner.origin.x,
135 bound_inner.origin.y,
136 bound_inner.size.width,
137 bound_inner.size.height
138 ).resize_exact(size.width, size.height, FilterType::Triangle)
139}
140
141fn sdf_overlay_aa(sdf: f64, Δp: f64, mut col1: Rgba<u8>, mut col2: Rgba<u8>) -> Rgba<u8> {
142 let Δf = (0.5 * Δp - sdf) .clamp(0.0, Δp);
144 let alpha = Δf / Δp;
145 col2.0[3] = ((col2.0[3] as f64) * alpha) as u8;
147 col1.blend(&col2);
148 col1
149}