11use std:: cmp:: min;
22
3+ use rayon:: prelude:: * ;
4+
35use crate :: { BlendMode , Color , Image , Mask , Point } ;
46
57use super :: blend:: { self , RgbaColor } ;
@@ -59,55 +61,51 @@ pub fn draw_layer_over_image(image: &mut Image, layer: &Layer) {
5961 0
6062 } ;
6163
62- let y_offset = if location. y < 0 {
63- location. y . abs ( ) as u32
64- } else {
65- 0
66- } ;
67-
68- // I tried using rayon for this, but with 10,000 rows the performance
69- // was a little worse with rayon than without.
70- for y in 0 ..required_height {
71- let y_position = y + y_offset;
72- let y_position = ( y_position as f32 * pixel_ratio_y) . floor ( ) as u32 ;
73- let offset = ( y_position * layer_bytes_per_row) as usize ; //+ y_offset;
74- let target_offset = ( ( target_y_offset + y) * image. bytes_per_row ) as i32 ;
75- let target_offset = ( target_offset + ( start_x as i32 ) * 4 ) as usize ;
76- // Using a second loop was a tiny bit faster than splicing the vec.
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 ;
64+ image
65+ . data
66+ . par_chunks_mut ( image. bytes_per_row as usize )
67+ . enumerate ( )
68+ . skip ( target_y_offset as usize ) // Skip rows outside the target area
69+ . take ( required_height as usize ) // Only process the necessary rows
70+ . for_each ( |( y, row) | {
71+ let y = y as u32 ;
72+ let y_position = y as i32 - location. y ;
73+ let y_position = ( y_position as f32 * pixel_ratio_y) . floor ( ) as u32 ;
74+ let offset = ( y_position * layer_bytes_per_row) as usize ;
75+
76+ for x in 0 ..required_width {
77+ let alpha = layer. mask_alpha ( Point {
78+ x : x as u32 ,
79+ y : y - target_y_offset,
80+ } ) ;
81+ if alpha == 0 {
82+ continue ;
83+ }
84+ let mask_opacity = alpha as f32 / u8:: MAX as f32 ;
85+ let x = x * 4 ;
86+ let x_position = x + x_offset;
87+ let x_position = ( x_position as f32 * pixel_ratio_x) . floor ( ) as usize ;
88+ let start = offset + x_position;
89+ let blend_color = pixel_data ( & layer. image . data , start) ;
90+ let blend_color: Color = blend_color. into ( ) ;
91+
92+ let start = start_x as usize * 4 + x;
93+ let base_color = pixel_data ( & row, start) ;
94+ let mut base_color: Color = base_color. into ( ) ;
95+
96+ blend_colors (
97+ & mut base_color,
98+ & blend_color,
99+ layer. blend_mode ,
100+ layer. opacity * mask_opacity,
101+ ) ;
102+
103+ row[ start + 0 ] = base_color. red ;
104+ row[ start + 1 ] = base_color. green ;
105+ row[ start + 2 ] = base_color. blue ;
106+ row[ start + 3 ] = base_color. alpha ;
84107 }
85- let mask_opacity = alpha as f32 / u8:: MAX as f32 ;
86- let x = x * 4 ;
87- let x_position = x + x_offset;
88- let x_position = ( x_position as f32 * pixel_ratio_x) . floor ( ) as usize ;
89- let start = offset + x_position;
90- let blend_color = pixel_data ( & layer. image . data , start) ;
91- let blend_color: Color = blend_color. into ( ) ;
92-
93- let start = target_offset + x;
94- let base_color = pixel_data ( & image. data , start) ;
95- let mut base_color: Color = base_color. into ( ) ;
96-
97- blend_colors (
98- & mut base_color,
99- & blend_color,
100- layer. blend_mode ,
101- layer. opacity * mask_opacity,
102- ) ;
103- // let base_color = Color::RED;
104-
105- image. data [ target_offset + x + 0 ] = base_color. red ;
106- image. data [ target_offset + x + 1 ] = base_color. green ;
107- image. data [ target_offset + x + 2 ] = base_color. blue ;
108- image. data [ target_offset + x + 3 ] = base_color. alpha ;
109- }
110- }
108+ } ) ;
111109}
112110
113111impl Image {
@@ -126,11 +124,12 @@ impl Image {
126124}
127125
128126/// Retrieves the pixel data from a given location in a vector of RGBA bytes.
129- fn pixel_data ( source : & Vec < u8 > , offset : usize ) -> [ u8 ; 4 ] {
127+ fn pixel_data ( source : & [ u8 ] , offset : usize ) -> [ u8 ; 4 ] {
130128 source
131129 . get ( offset..( offset + 4 ) )
132130 . and_then ( |data| data. try_into ( ) . ok ( ) )
133- . unwrap_or_default ( )
131+ . unwrap_or ( [ u8:: MAX , 0 , 0 , u8:: MAX ] )
132+ // .unwrap_or_default()
134133}
135134
136135/// Blends one colour with another.
@@ -300,7 +299,7 @@ mod test {
300299 layer. position . x += 1.0 ;
301300 draw_layer_over_image ( & mut base_image, & layer) ;
302301
303- // base_image.save("/tmp/tiled_mask.png").unwrap();
302+ base_image. save ( "/tmp/tiled_mask.png" ) . unwrap ( ) ;
304303
305304 let expected_image = Image :: open ( "tests/images/tiled_mask.png" ) . unwrap ( ) ;
306305
@@ -346,10 +345,39 @@ mod test {
346345 layer. position . x -= 1.0 ;
347346 draw_layer_over_image ( & mut base_image, & layer) ;
348347
349- // base_image.save("/tmp/tiled_mask_negative.png").unwrap();
348+ base_image. save ( "/tmp/tiled_mask_negative.png" ) . unwrap ( ) ;
350349
351350 let expected_image = Image :: open ( "tests/images/tiled_mask_negative.png" ) . unwrap ( ) ;
352351
353352 assert ! ( base_image. appears_equal_to( & expected_image) ) ;
354353 }
354+
355+ #[ test]
356+ #[ ignore]
357+ fn draw_layer_performance ( ) {
358+ let size = Size {
359+ width : 10 ,
360+ height : 10 ,
361+ } ;
362+ // let size = Size {
363+ // width: 4,
364+ // height: 4,
365+ // };
366+ let mut base_image = Image :: color ( & Color :: from_rgb_u32 ( 0x639bff ) , size) ;
367+ let mut image = Image :: color ( & Color :: from_rgb_u32 ( 0xcbdbfc ) , size) ;
368+ image. set_pixel_color ( Color :: CLEAR , Point { x : 2 , y : 1 } ) ;
369+ image. set_pixel_color ( Color :: GREEN , Point { x : 0 , y : 0 } ) ;
370+ image. set_pixel_color ( Color :: CYAN , Point { x : 1 , y : 0 } ) ;
371+ // image.save("/tmp/loco.png").unwrap();
372+ let position = Point { x : 0.0 , y : -1.0 } ;
373+ let layer = Layer :: new ( & image, position) ;
374+
375+ let now = std:: time:: Instant :: now ( ) ;
376+ draw_layer_over_image ( & mut base_image, & layer) ;
377+ // 670ms
378+ println ! ( "🍟 time taken: {:?}" , now. elapsed( ) ) ;
379+
380+ base_image. save ( "/tmp/base_image.png" ) . unwrap ( ) ;
381+ panic ! ( ) ;
382+ }
355383}
0 commit comments