1use crate::*;
2
3#[derive(Clone, Copy, Default, Debug)]
5#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
6struct Convert<T: Color>(std::marker::PhantomData<T>);
7
8pub fn convert<T: Type, C: Color, U: Type, D: Color>() -> impl Filter<T, C, U, D> {
10 Convert(std::marker::PhantomData)
11}
12
13impl<T: Type, C: Color, U: Type, D: Color> Filter<T, C, U, D> for Convert<D> {
14 fn compute_at(&self, pt: Point, input: &Input<T, C>, dest: &mut DataMut<U, D>) {
15 input.get_pixel(pt, None).convert_to_data(dest);
16 }
17}
18
19#[derive(Debug, Default)]
20#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
21struct Saturation(pub f64);
22
23pub fn saturation<T: Type, C: Color, U: Type, D: Color>(amt: f64) -> impl Filter<T, C, U, D> {
25 Saturation(amt)
26}
27
28impl<T: Type, C: Color, U: Type, D: Color> Filter<T, C, U, D> for Saturation {
29 fn compute_at(&self, pt: Point, input: &Input<T, C>, data: &mut DataMut<U, D>) {
30 let px = input.get_pixel(pt, None);
31 let mut tmp: Pixel<Hsv> = px.convert();
32 tmp[1] *= self.0;
33 tmp.convert_to_data(data);
34 }
35}
36
37#[derive(Debug)]
38#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
39struct Brightness(f64);
40
41pub fn brightness<T: Type, C: Color, U: Type, D: Color>(amt: f64) -> impl Filter<T, C, U, D> {
43 Brightness(amt)
44}
45
46impl<T: Type, C: Color, U: Type, D: Color> Filter<T, C, U, D> for Brightness {
47 fn compute_at(&self, pt: Point, input: &Input<T, C>, data: &mut DataMut<U, D>) {
48 let mut px = input.get_pixel(pt, None);
49 px *= self.0;
50 px.convert_to_data(data);
51 }
52}
53
54#[derive(Debug)]
55#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
56struct Exposure(f64);
57
58pub fn exposure<T: Type, C: Color, U: Type, D: Color>(stops: f64) -> impl Filter<T, C, U, D> {
60 Exposure(stops)
61}
62
63impl<T: Type, C: Color, U: Type, D: Color> Filter<T, C, U, D> for Exposure {
64 fn compute_at(&self, pt: Point, input: &Input<T, C>, data: &mut DataMut<U, D>) {
65 let mut px = input.get_pixel(pt, None);
66 px *= 2f64.powf(self.0);
67 px.convert_to_data(data);
68 }
69}
70
71#[derive(Debug)]
72#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
73struct Contrast(pub f64);
74
75pub fn contrast<T: Type, C: Color, U: Type, D: Color>(amt: f64) -> impl Filter<T, C, U, D> {
77 Contrast(amt)
78}
79
80impl<T: Type, C: Color, U: Type, D: Color> Filter<T, C, U, D> for Contrast {
81 fn compute_at(&self, pt: Point, input: &Input<T, C>, data: &mut DataMut<U, D>) {
82 let mut px = input.get_pixel(pt, None);
83 px.map(|x| (self.0 * (x - 0.5)) + 0.5);
84 px.convert_to_data(data);
85 }
86}
87
88#[derive(Debug)]
89#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
90struct Crop(Region);
91
92pub fn crop<T: Type, C: Color, U: Type, D: Color>(r: Region) -> impl Filter<T, C, U, D> {
94 Crop(r)
95}
96
97impl<T: Type, C: Color, U: Type, D: Color> Filter<T, C, U, D> for Crop {
98 fn output_size(&self, _input: &Input<T, C>, _dest: &mut Image<U, D>) -> Size {
99 self.0.size
100 }
101
102 fn compute_at(&self, pt: Point, input: &Input<T, C>, dest: &mut DataMut<U, D>) {
103 if pt.x > self.0.origin.x + self.0.size.width || pt.y > self.0.origin.y + self.0.size.height
104 {
105 return;
106 }
107
108 let x = pt.x + self.0.origin.x;
109 let y = pt.y + self.0.origin.y;
110 let px = input.get_pixel((x, y), None);
111 px.copy_to_slice(dest);
112 }
113}
114
115#[derive(Debug)]
116#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
117struct Invert;
118
119pub fn invert<T: Type, C: Color, U: Type, D: Color>() -> impl Filter<T, C, U, D> {
121 Invert
122}
123
124impl<T: Type, C: Color, U: Type, D: Color> Filter<T, C, U, D> for Invert {
125 fn compute_at(&self, pt: Point, input: &Input<T, C>, dest: &mut DataMut<U, D>) {
126 let mut px = input.get_pixel(pt, None);
127 px.map(|x| 1.0 - x);
128 px.copy_to_slice(dest);
129 }
130}
131
132#[derive(Debug)]
133#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
134struct Blend;
135
136pub fn blend<T: Type, C: Color, U: Type, D: Color>() -> impl Filter<T, C, U, D> {
138 Blend
139}
140
141impl<T: Type, C: Color, U: Type, D: Color> Filter<T, C, U, D> for Blend {
142 fn compute_at(&self, pt: Point, input: &Input<T, C>, dest: &mut DataMut<U, D>) {
143 let a = input.get_pixel(pt, None);
144 let b = input.get_pixel(pt, Some(1));
145 ((a + &b) / 2.).copy_to_slice(dest);
146 }
147}
148
149#[derive(Debug)]
150#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
151struct GammaLog(f64);
152
153pub fn gamma_log<T: Type, C: Color, U: Type, D: Color>(
155 gamma: Option<f64>,
156) -> impl Filter<T, C, U, D> {
157 GammaLog(gamma.unwrap_or(2.2))
158}
159
160impl<T: Type, C: Color, U: Type, D: Color> Filter<T, C, U, D> for GammaLog {
161 fn compute_at(&self, pt: Point, input: &Input<T, C>, dest: &mut DataMut<U, D>) {
162 let mut px = input.get_pixel(pt, None);
163 px.map(|x| x.powf(1.0 / self.0));
164 px.copy_to_slice(dest);
165 }
166}
167
168#[derive(Debug)]
169#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
170struct GammaLin(f64);
171
172pub fn gamma_lin<T: Type, C: Color, U: Type, D: Color>(
174 gamma: Option<f64>,
175) -> impl Filter<T, C, U, D> {
176 GammaLin(gamma.unwrap_or(2.2))
177}
178
179impl<T: Type, C: Color, U: Type, D: Color> Filter<T, C, U, D> for GammaLin {
180 fn compute_at(&self, pt: Point, input: &Input<T, C>, dest: &mut DataMut<U, D>) {
181 let mut px = input.get_pixel(pt, None);
182 px.map(|x| x.powf(self.0));
183 px.copy_to_slice(dest);
184 }
185}
186
187struct If<
189 F: Fn(Point, &Input<T, C>) -> bool,
190 G: Filter<T, C, U, D>,
191 H: Filter<T, C, U, D>,
192 T: Type,
193 C: Color,
194 U: Type,
195 D: Color,
196> {
197 cond: F,
198 then: G,
199 else_: H,
200 _t: std::marker::PhantomData<(T, C, U, D)>,
201}
202
203pub fn if_then_else<
205 F: Sync + Fn(Point, &Input<T, C>) -> bool,
206 G: Filter<T, C, U, D>,
207 H: Filter<T, C, U, D>,
208 T: Type,
209 C: Color,
210 U: Type,
211 D: Color,
212>(
213 cond: F,
214 then: G,
215 else_: H,
216) -> impl Filter<T, C, U, D> {
217 If {
218 cond,
219 then,
220 else_,
221 _t: std::marker::PhantomData,
222 }
223}
224
225impl<
226 F: Fn(Point, &Input<T, C>) -> bool,
227 G: Filter<T, C, U, D>,
228 H: Filter<T, C, U, D>,
229 T: Type,
230 C: Color,
231 U: Type,
232 D: Color,
233 > std::fmt::Debug for If<F, G, H, T, C, U, D>
234{
235 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
236 fmt.debug_struct("If")
237 .field("cond", &"Function")
238 .field("then", &self.then)
239 .field("else", &self.else_)
240 .finish()
241 }
242}
243
244impl<
245 F: Sync + Fn(Point, &Input<T, C>) -> bool,
246 G: Filter<T, C, U, D>,
247 H: Filter<T, C, U, D>,
248 T: Type,
249 C: Color,
250 U: Type,
251 D: Color,
252 > Filter<T, C, U, D> for If<F, G, H, T, C, U, D>
253{
254 fn schedule(&self) -> Schedule {
255 if self.then.schedule() == Schedule::Image || self.else_.schedule() == Schedule::Image {
256 return Schedule::Image;
257 }
258
259 Schedule::Pixel
260 }
261
262 fn compute_at(&self, pt: Point, input: &Input<T, C>, dest: &mut DataMut<U, D>) {
263 if (self.cond)(pt, input) {
264 self.then.compute_at(pt, input, dest)
265 } else {
266 self.else_.compute_at(pt, input, dest)
267 }
268 }
269}
270
271#[derive(Debug)]
272#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
273struct Clamp;
274
275pub fn clamp<T: Type, C: Color, U: Type, D: Color>() -> impl Filter<T, C, U, D> {
277 Clamp
278}
279
280impl<T: Type, C: Color, U: Type, D: Color> Filter<T, C, U, D> for Clamp {
281 fn compute_at(&self, pt: Point, input: &Input<T, C>, dest: &mut DataMut<U, D>) {
282 input.get_pixel(pt, None).clamped().copy_to_slice(dest)
283 }
284}
285
286#[derive(Debug)]
287#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
288struct Normalize {
289 min: f64,
290 max: f64,
291 new_min: f64,
292 new_max: f64,
293}
294
295pub fn normalize<T: Type, C: Color, U: Type, D: Color>(
297 min: f64,
298 max: f64,
299 new_min: f64,
300 new_max: f64,
301) -> impl Filter<T, C, U, D> {
302 Normalize {
303 min,
304 max,
305 new_min,
306 new_max,
307 }
308}
309
310impl<T: Type, C: Color, U: Type, D: Color> Filter<T, C, U, D> for Normalize {
311 fn compute_at(&self, pt: Point, input: &Input<T, C>, dest: &mut DataMut<U, D>) {
312 input
313 .get_pixel(pt, None)
314 .map(|x| {
315 (x - self.min) * ((self.new_max - self.new_min) / (self.max - self.min))
316 + self.new_min
317 })
318 .copy_to_slice(dest)
319 }
320}
321
322#[derive(Debug)]
323#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
324struct Noop;
325
326pub fn noop<T: Type, C: Color, U: Type, D: Color>() -> impl Filter<T, C, U, D> {
328 Noop
329}
330
331impl<T: Type, C: Color, U: Type, D: Color> Filter<T, C, U, D> for Noop {
332 fn compute_at(&self, pt: Point, input: &Input<T, C>, dest: &mut DataMut<U, D>) {
333 input.get_pixel(pt, None).copy_to_slice(dest)
334 }
335}
336
337#[inline]
338pub fn rotate<T: Type, C: Color, U: Type, D: Color>(
340 deg: f64,
341 center: Point,
342) -> impl Filter<T, C, U, D> {
343 let center = center.to_tuple();
344 Transform::rotation(euclid::Angle::degrees(-deg))
345 .pre_translate(euclid::Vector2D::new(
346 -(center.0 as f64),
347 -(center.1 as f64),
348 ))
349 .then_translate(euclid::Vector2D::new(center.0 as f64, center.1 as f64))
350}
351
352#[inline]
353pub fn scale<T: Type, C: Color, U: Type, D: Color>(x: f64, y: f64) -> impl Filter<T, C, U, D> {
355 Transform::scale(1.0 / x, 1.0 / y)
356}
357
358#[inline]
359pub fn resize<T: Type, C: Color, U: Type, D: Color>(
361 from: Size,
362 to: Size,
363) -> impl Filter<T, C, U, D> {
364 Transform::scale(
365 from.width as f64 / to.width as f64,
366 from.height as f64 / to.height as f64,
367 )
368}
369
370pub fn rotate90<T: Type, C: Color, U: Type, D: Color>(
372 from: Size,
373 to: Size,
374) -> impl Filter<T, C, U, D> {
375 let dwidth = to.width as f64;
376 let height = from.height as f64;
377 rotate(
378 90.,
379 Point::new((dwidth / 2.) as usize, (height / 2.) as usize),
380 )
381}
382
383pub fn rotate180<T: Type, C: Color, U: Type, D: Color>(src: Size) -> impl Filter<T, C, U, D> {
385 let dwidth = src.width as f64;
386 let height = src.height as f64;
387 rotate(
388 180.,
389 Point::new((dwidth / 2.) as usize, (height / 2.) as usize),
390 )
391}
392
393pub fn rotate270<T: Type, C: Color, U: Type, D: Color>(
395 from: Size,
396 to: Size,
397) -> impl Filter<T, C, U, D> {
398 let width = to.height as f64;
399 let dheight = from.width as f64;
400 rotate(
401 270.,
402 Point::new((width / 2.) as usize, (dheight / 2.) as usize),
403 )
404}