1use crate::{point, size, Point, Size, Surface, SurfaceMut};
2
3#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
6#[non_exhaustive]
7pub enum Transform {
8 UpScale {
11 x: u32,
13 y: u32,
15 },
16
17 Rotate90Cw,
19 Rotate90Ccw,
21 Rotate180,
23
24 FlipHorizontal,
26 FlipVertical,
28 FlipBoth,
30}
31
32impl Transform {
33 #[inline]
34 #[allow(dead_code)]
35 fn apply((pt, size): (Point, Size), this: &Self) -> (Point, Size) {
36 use Transform::*;
37
38 let pt = match this {
39 UpScale { x, y } => point(pt.x * x, pt.y * y),
40
41 FlipHorizontal => point(reversed(pt.x, size.x), pt.y),
42 FlipVertical => point(pt.x, reversed(pt.y, size.y)),
43 FlipBoth => point(reversed(pt.x, size.x), reversed(pt.y, size.y)),
44
45 Rotate90Ccw => point(pt.y, reversed(pt.x, size.x)),
46 Rotate90Cw => point(reversed(pt.y, size.y), pt.x),
47 Rotate180 => point(reversed(pt.x, size.x), reversed(pt.y, size.y)),
48 };
49
50 (pt, Self::apply_size(size, this))
51 }
52
53 #[inline]
54 fn unapply((pt, size): (Point, Size), this: &Self) -> (Point, Size) {
55 use Transform::*;
56
57 let pt = match this {
58 UpScale { x, y } => point(pt.x / x, pt.y / y),
59
60 FlipHorizontal => point(reversed(pt.x, size.x), pt.y),
62 FlipVertical => point(pt.x, reversed(pt.y, size.y)),
63 FlipBoth => point(reversed(pt.x, size.x), reversed(pt.y, size.y)),
64 Rotate180 => point(reversed(pt.x, size.x), reversed(pt.y, size.y)),
65
66 Rotate90Cw => point(pt.y, reversed(pt.x, size.x)),
68 Rotate90Ccw => point(reversed(pt.y, size.y), pt.x),
69 };
70
71 (pt, Self::unapply_size(size, this))
72 }
73
74 #[inline]
75 fn apply_size(s: Size, this: &Self) -> Size {
76 use Transform::*;
77
78 match this {
79 UpScale { x, y } => size(s.x * x, s.y * y),
80 Rotate90Cw | Rotate90Ccw => size(s.y, s.x),
81 _ => s,
82 }
83 }
84
85 #[inline]
86 fn unapply_size(s: Size, this: &Self) -> Size {
87 use Transform::*;
88
89 match this {
90 UpScale { x, y } => size(s.x / x, s.y / y),
91 Rotate90Cw | Rotate90Ccw => size(s.y, s.x),
92 _ => s,
93 }
94 }
95}
96
97#[inline]
98fn reversed(coord: u32, size: u32) -> u32 {
99 size.saturating_sub(coord).saturating_sub(1)
100}
101
102pub fn blit_with<D, S>(
108 mut dest: impl SurfaceMut<D>,
109 src: impl Surface<S>,
110 transforms: &[Transform],
111 mut func: impl FnMut(&mut D, &S, Point),
112) {
113 let copy_size = src.surface_size();
114 let transformed_copy_size = transforms.iter().fold(copy_size, Transform::apply_size);
115
116 for iy in 0..transformed_copy_size.y {
117 for ix in 0..transformed_copy_size.x {
118 let dest_val_pos = point(ix, iy);
119
120 let dest = if let Some(dest) = dest.surface_get_mut(dest_val_pos) {
121 dest
122 } else {
123 continue;
124 };
125
126 let (src_val_pos, _untransformed_copy_size) = transforms
127 .iter()
128 .rev()
129 .fold((point(ix, iy), transformed_copy_size), Transform::unapply);
130
131 let src = if let Some(src) = src.surface_get(src_val_pos) {
132 src
133 } else {
134 continue;
135 };
136
137 (func)(dest, src, src_val_pos);
138 }
139 }
140}
141
142#[inline]
147pub fn blit<T: Clone>(dest: impl SurfaceMut<T>, src: impl Surface<T>, transforms: &[Transform]) {
148 blit_with(dest, src, transforms, |dest, src, _| {
149 dest.clone_from(src);
150 });
151}
152
153#[inline]
158pub fn blit_masked<T: Clone + PartialEq>(
159 dest: impl SurfaceMut<T>,
160 src: impl Surface<T>,
161 transforms: &[Transform],
162 mask: &T,
163) {
164 blit_with(dest, src, transforms, |dest, src, _| {
165 if src != mask {
166 dest.clone_from(src);
167 }
168 });
169}
170
171#[inline]
176pub fn blit_convert<D: From<S>, S: Clone>(
177 dest: impl SurfaceMut<D>,
178 src: impl Surface<S>,
179 transforms: &[Transform],
180) {
181 blit_with(dest, src, transforms, |dest, src, _| {
182 *dest = D::from(src.clone());
183 });
184}