11use std:: cmp:: min;
22
3- use crate :: { BlendMode , Color , Image , Point } ;
3+ use crate :: { BlendMode , Color , Image , Mask , Point } ;
44
55use super :: blend:: { self , RgbaColor } ;
66use super :: operation:: Operation ;
@@ -74,16 +74,18 @@ pub fn draw_layer_over_image(image: &mut Image, layer: &Layer) {
7474 let target_offset = ( ( target_y_offset + y) * image. bytes_per_row ) as i32 ;
7575 let target_offset = ( target_offset + ( start_x as i32 ) * 4 ) as usize ;
7676 // Using a second loop was a tiny bit faster than splicing the vec.
77- for x in ( 0 ..required_width * 4 ) . step_by ( 4 ) {
77+ for x in 0 ..required_width {
78+ let alpha = layer. mask_alpha ( Point {
79+ x : x as u32 ,
80+ y : y as u32 ,
81+ } ) ;
82+ if alpha == 0 {
83+ continue ;
84+ }
85+ let mask_opacity = alpha as f32 / u8:: MAX as f32 ;
86+ let x = x * 4 ;
7887 let x_position = x + x_offset;
7988 let x_position = ( x_position as f32 * pixel_ratio_x) . floor ( ) as usize ;
80- if layer. mask_alpha ( Point {
81- x : x_position as u32 ,
82- y : y_position as u32 ,
83- } ) == 0
84- {
85- break ;
86- }
8789 let start = offset + x_position;
8890 let blend_color = pixel_data ( & layer. image . data , start) ;
8991 let blend_color: Color = blend_color. into ( ) ;
@@ -96,7 +98,7 @@ pub fn draw_layer_over_image(image: &mut Image, layer: &Layer) {
9698 & mut base_color,
9799 & blend_color,
98100 layer. blend_mode ,
99- layer. opacity ,
101+ layer. opacity * mask_opacity ,
100102 ) ;
101103 // let base_color = Color::RED;
102104
@@ -118,6 +120,7 @@ impl Image {
118120 ) {
119121 let mut layer = Layer :: new ( image, location. into ( ) ) ;
120122 layer. blend_mode = options. blend_mode . to_owned ( ) ;
123+ layer. masks = options. masks . to_owned ( ) ;
121124 draw_layer_over_image ( self , & layer) ;
122125 }
123126}
@@ -213,12 +216,16 @@ fn blend_colors(color: &mut Color, blend_color: &Color, blend_mode: BlendMode, o
213216pub struct CompositeOperationOptions < ' a > {
214217 /// The blend mode.
215218 pub blend_mode : BlendMode ,
216- /// The mask image .
217- pub mask_image : Option < & ' a Image > ,
219+ /// The masks .
220+ pub masks : Vec < Mask < ' a > > ,
218221}
219222
220223#[ cfg( test) ]
221224mod test {
225+ use std:: borrow:: Cow ;
226+
227+ use crate :: { Mask , Size , TiledMask } ;
228+
222229 use super :: * ;
223230
224231 #[ test]
@@ -251,4 +258,98 @@ mod test {
251258 assert_eq ! ( color. blue, 0xff , "Blues don’t match." ) ;
252259 assert_eq ! ( color. alpha, 153 , "Alphas don’t match." ) ;
253260 }
261+
262+ #[ test]
263+ fn draw_layer_with_tiled_mask ( ) {
264+ let mut base_image = Image :: color (
265+ & Color :: from_rgb_u32 ( 0x639bff ) ,
266+ Size {
267+ width : 16 ,
268+ height : 8 ,
269+ } ,
270+ ) ;
271+ let image = Image :: color (
272+ & Color :: from_rgb_u32 ( 0xcbdbfc ) ,
273+ Size {
274+ width : 13 ,
275+ height : 6 ,
276+ } ,
277+ ) ;
278+ let position = Point { x : 1.0 , y : 1.0 } ;
279+ let mut layer = Layer :: new ( & image, position) ;
280+
281+ // Setting up a checkerboard image.
282+ let mut image = Image :: empty ( Size {
283+ width : 2 ,
284+ height : 2 ,
285+ } ) ;
286+ image. set_pixel_color ( Color :: BLACK , Point :: zero ( ) ) ;
287+ image. set_pixel_color ( Color :: BLACK , Point { x : 1 , y : 1 } ) ;
288+
289+ let mask = TiledMask {
290+ image : Cow :: Borrowed ( & image) ,
291+ offset : Point { x : 1 , y : 0 } ,
292+ } ;
293+ let mask = Mask :: Tiled ( mask) ;
294+ layer. masks = vec ! [ mask] ;
295+
296+ draw_layer_over_image ( & mut base_image, & layer) ;
297+
298+ // Reposition the layer and draw again to make sure
299+ // that the mask stays put.
300+ layer. position . x += 1.0 ;
301+ draw_layer_over_image ( & mut base_image, & layer) ;
302+
303+ // base_image.save("/tmp/tiled_mask.png").unwrap();
304+
305+ let expected_image = Image :: open ( "tests/images/tiled_mask.png" ) . unwrap ( ) ;
306+
307+ assert ! ( base_image. appears_equal_to( & expected_image) ) ;
308+ }
309+
310+ #[ test]
311+ fn draw_layer_with_tiled_mask_negative_position ( ) {
312+ let mut base_image = Image :: color (
313+ & Color :: from_rgb_u32 ( 0x639bff ) ,
314+ Size {
315+ width : 16 ,
316+ height : 8 ,
317+ } ,
318+ ) ;
319+ let image = Image :: color (
320+ & Color :: from_rgb_u32 ( 0xcbdbfc ) ,
321+ Size {
322+ width : 13 ,
323+ height : 6 ,
324+ } ,
325+ ) ;
326+ let position = Point { x : -1.0 , y : -1.0 } ;
327+ let mut layer = Layer :: new ( & image, position) ;
328+
329+ // Setting up a checkerboard image.
330+ let mut image = Image :: empty ( Size {
331+ width : 3 ,
332+ height : 3 ,
333+ } ) ;
334+ image. set_pixel_color ( Color :: BLACK , Point :: zero ( ) ) ;
335+ image. set_pixel_color ( Color :: BLACK , Point { x : 1 , y : 1 } ) ;
336+
337+ let mask = TiledMask {
338+ image : Cow :: Borrowed ( & image) ,
339+ offset : Point { x : 0 , y : 0 } ,
340+ } ;
341+ let mask = Mask :: Tiled ( mask) ;
342+ layer. masks = vec ! [ mask] ;
343+
344+ draw_layer_over_image ( & mut base_image, & layer) ;
345+
346+ layer. position . x -= 1.0 ;
347+ draw_layer_over_image ( & mut base_image, & layer) ;
348+
349+ // base_image.save("/tmp/tiled_mask_negative.png").unwrap();
350+
351+ let expected_image = Image :: open ( "tests/images/tiled_mask_negative.png" ) . unwrap ( ) ;
352+
353+ assert ! ( base_image. appears_equal_to( & expected_image) ) ;
354+ }
254355}
0 commit comments