Skip to content

Commit 43a9ea8

Browse files
committed
🎏 Got the tiled mask working.
1 parent 2229bd4 commit 43a9ea8

File tree

2 files changed

+200
-13
lines changed

2 files changed

+200
-13
lines changed

src/composite/layer.rs

Lines changed: 197 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,6 @@ pub struct Layer<'a> {
2020
pub masks: Vec<Mask<'a>>,
2121
}
2222

23-
/// Defines a property that can be either owned or borrowed.
24-
#[derive(Debug, Clone)]
25-
pub enum Either<'a, T> {
26-
/// The owned value.
27-
Owned(T),
28-
/// The borrowed value.
29-
Borrowed(&'a T),
30-
}
31-
3223
// MARK: Creation
3324

3425
impl<'a> Layer<'a> {
@@ -63,13 +54,209 @@ impl<'a> Layer<'a> {
6354

6455
impl<'a> Layer<'a> {
6556
/// Returns the alpha for the mask at a given location.
57+
///
58+
/// If the layer should be fully opaque at this location,
59+
/// u8::MAX is returned.
60+
/// If it should be fully transparent, 0 is returned,
6661
/// If there is no mask, u8::MAX is returned.
6762
pub fn mask_alpha(&self, location: Point<u32>) -> u8 {
6863
let mut result = u8::MAX;
6964

65+
let mut location = location.into();
7066
for mask in self.masks.iter() {
71-
// result = ()
67+
let color = match mask {
68+
Mask::Positioned(positioned_mask) => {
69+
location -= positioned_mask.origin;
70+
positioned_mask.image.pixel_color(location)
71+
}
72+
Mask::Tiled(tiled_mask) => {
73+
location -= tiled_mask.offset;
74+
let width = tiled_mask.image.size.width as i32;
75+
let height = tiled_mask.image.size.height as i32;
76+
if location.x < 0 {
77+
location.x = width + (location.x % width);
78+
}
79+
if location.x >= width {
80+
location.x = location.x % width;
81+
}
82+
if location.y < 0 {
83+
location.y = height + (location.y % height);
84+
}
85+
if location.y >= height {
86+
location.y = location.y % height;
87+
}
88+
tiled_mask.image.pixel_color(location)
89+
}
90+
};
91+
// Out of bounds.
92+
let Some(color) = color else {
93+
return 0;
94+
};
95+
let alpha = (result as u16 * color.alpha as u16) / u8::MAX as u16;
96+
result = alpha as u8;
7297
}
7398
result
7499
}
75100
}
101+
102+
#[cfg(test)]
103+
mod test {
104+
use std::borrow::Cow;
105+
106+
use crate::{Color, Image, Mask, Point, PositionedMask, Size, TiledMask};
107+
108+
use super::Layer;
109+
110+
#[test]
111+
fn mask_alpha_no_masks() {
112+
let image = Image::color(
113+
&Color::MAGENTA,
114+
Size {
115+
width: 13,
116+
height: 21,
117+
},
118+
);
119+
let position = Point { x: 3.0, y: 5.0 };
120+
let layer = Layer::new(&image, position);
121+
122+
let location = Point { x: 4, y: 9 };
123+
let alpha = layer.mask_alpha(location);
124+
125+
assert_eq!(alpha, u8::MAX);
126+
}
127+
128+
#[test]
129+
fn mask_alpha_positioned_mask() {
130+
let image = Image::color(
131+
&Color::MAGENTA,
132+
Size {
133+
width: 13,
134+
height: 21,
135+
},
136+
);
137+
let position = Point { x: 3.0, y: 5.0 };
138+
let mut layer = Layer::new(&image, position);
139+
140+
let image = Image::color(
141+
&Color::BLACK,
142+
Size {
143+
width: 4,
144+
height: 3,
145+
},
146+
);
147+
let mask = PositionedMask {
148+
image: Cow::Borrowed(&image),
149+
origin: Point { x: 2, y: 1 },
150+
};
151+
let mask = Mask::Positioned(mask);
152+
layer.masks = vec![mask];
153+
154+
let location = Point { x: 4, y: 3 };
155+
let alpha = layer.mask_alpha(location);
156+
assert_eq!(alpha, u8::MAX);
157+
158+
let location = Point { x: 0, y: 0 };
159+
let alpha = layer.mask_alpha(location);
160+
assert_eq!(alpha, 0);
161+
}
162+
163+
#[test]
164+
fn mask_alpha_tiled_mask() {
165+
let image = Image::color(
166+
&Color::MAGENTA,
167+
Size {
168+
width: 13,
169+
height: 21,
170+
},
171+
);
172+
let position = Point { x: 3.0, y: 5.0 };
173+
let mut layer = Layer::new(&image, position);
174+
175+
// Setting up a checkerboard image.
176+
let mut image = Image::empty(Size {
177+
width: 2,
178+
height: 2,
179+
});
180+
image.set_pixel_color(Color::BLACK, Point::zero());
181+
image.set_pixel_color(Color::BLACK, Point { x: 1, y: 1 });
182+
183+
let mask = TiledMask {
184+
image: Cow::Borrowed(&image),
185+
offset: Point { x: 1, y: 0 },
186+
};
187+
let mask = Mask::Tiled(mask);
188+
layer.masks = vec![mask];
189+
190+
let location = Point { x: 0, y: 0 };
191+
let alpha = layer.mask_alpha(location);
192+
assert_eq!(alpha, 0);
193+
194+
let location = Point { x: 1, y: 0 };
195+
let alpha = layer.mask_alpha(location);
196+
assert_eq!(alpha, u8::MAX);
197+
198+
let location = Point { x: 2, y: 0 };
199+
let alpha = layer.mask_alpha(location);
200+
assert_eq!(alpha, 0);
201+
202+
let location = Point { x: 3, y: 0 };
203+
let alpha = layer.mask_alpha(location);
204+
assert_eq!(alpha, u8::MAX);
205+
206+
let location = Point { x: 4, y: 0 };
207+
let alpha = layer.mask_alpha(location);
208+
assert_eq!(alpha, 0);
209+
210+
let location = Point { x: 5, y: 0 };
211+
let alpha = layer.mask_alpha(location);
212+
assert_eq!(alpha, u8::MAX);
213+
214+
let location = Point { x: 0, y: 2 };
215+
let alpha = layer.mask_alpha(location);
216+
assert_eq!(alpha, 0);
217+
218+
let location = Point { x: 1, y: 2 };
219+
let alpha = layer.mask_alpha(location);
220+
assert_eq!(alpha, u8::MAX);
221+
222+
let location = Point { x: 2, y: 2 };
223+
let alpha = layer.mask_alpha(location);
224+
assert_eq!(alpha, 0);
225+
226+
let location = Point { x: 3, y: 2 };
227+
let alpha = layer.mask_alpha(location);
228+
assert_eq!(alpha, u8::MAX);
229+
230+
let location = Point { x: 4, y: 2 };
231+
let alpha = layer.mask_alpha(location);
232+
assert_eq!(alpha, 0);
233+
234+
let location = Point { x: 5, y: 2 };
235+
let alpha = layer.mask_alpha(location);
236+
assert_eq!(alpha, u8::MAX);
237+
238+
let location = Point { x: 0, y: 3 };
239+
let alpha = layer.mask_alpha(location);
240+
assert_eq!(alpha, u8::MAX);
241+
242+
let location = Point { x: 1, y: 3 };
243+
let alpha = layer.mask_alpha(location);
244+
assert_eq!(alpha, 0);
245+
246+
let location = Point { x: 2, y: 3 };
247+
let alpha = layer.mask_alpha(location);
248+
assert_eq!(alpha, u8::MAX);
249+
250+
let location = Point { x: 3, y: 3 };
251+
let alpha = layer.mask_alpha(location);
252+
assert_eq!(alpha, 0);
253+
254+
let location = Point { x: 4, y: 3 };
255+
let alpha = layer.mask_alpha(location);
256+
assert_eq!(alpha, u8::MAX);
257+
258+
let location = Point { x: 5, y: 3 };
259+
let alpha = layer.mask_alpha(location);
260+
assert_eq!(alpha, 0);
261+
}
262+
}

src/mask.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ pub enum Mask<'a> {
88
/// A positioned mask.
99
Positioned(PositionedMask<'a>),
1010
/// A tiled mask.
11-
Tiled(TiledMask),
11+
Tiled(TiledMask<'a>),
1212
}
1313

1414
pub trait Flamble: Clone + std::fmt::Debug {}
@@ -30,8 +30,8 @@ pub struct PositionedMask<'a> {
3030

3131
#[derive(Debug, Clone)]
3232
/// A mask image that is tiled across the canvas.
33-
pub struct TiledMask {
34-
pub image: Image,
33+
pub struct TiledMask<'a> {
34+
pub image: Cow<'a, Image>,
3535
pub offset: Point<i32>,
3636
}
3737

0 commit comments

Comments
 (0)