@@ -34,6 +34,7 @@ pub fn compare_images(
3434 threshold : f32 ,
3535 generate_diff : bool ,
3636 ignore_regions : & [ Region ] ,
37+ mask_path : Option < & Path > ,
3738) -> Result < DiffResult > {
3839 let img_a = image:: open ( path_a) ?;
3940 let img_b = image:: open ( path_b) ?;
@@ -44,8 +45,13 @@ pub fn compare_images(
4445 let max_width = width_a. max ( width_b) ;
4546 let max_height = height_a. max ( height_b) ;
4647
48+ let mask_img = if let Some ( path) = mask_path {
49+ Some ( image:: open ( path) ?. to_rgba8 ( ) )
50+ } else {
51+ None
52+ } ;
53+
4754 // For SSIM, we need identical dimensions.
48- // We'll use the max dimensions and pad with transparent pixels if needed.
4955 let mut rgba_a = img_a. to_rgba8 ( ) ;
5056 let mut rgba_b = img_b. to_rgba8 ( ) ;
5157
@@ -72,7 +78,19 @@ pub fn compare_images(
7278
7379 for y in 0 ..max_height {
7480 for x in 0 ..max_width {
75- let is_ignored = ignore_regions. iter ( ) . any ( |r| r. contains ( x, y) ) ;
81+ let mut is_ignored = ignore_regions. iter ( ) . any ( |r| r. contains ( x, y) ) ;
82+
83+ if !is_ignored {
84+ if let Some ( ref mask) = mask_img {
85+ if x < mask. width ( ) && y < mask. height ( ) {
86+ let mask_pixel = mask. get_pixel ( x, y) ;
87+ // Ignore if mask pixel is black or has low alpha
88+ if ( mask_pixel[ 0 ] == 0 && mask_pixel[ 1 ] == 0 && mask_pixel[ 2 ] == 0 ) || mask_pixel[ 3 ] < 128 {
89+ is_ignored = true ;
90+ }
91+ }
92+ }
93+ }
7694
7795 let pixel_a = rgba_a. get_pixel ( x, y) ;
7896 let pixel_b = rgba_b. get_pixel ( x, y) ;
@@ -160,7 +178,7 @@ mod tests {
160178 img. save ( file_a. path ( ) ) ?;
161179 img. save ( file_b. path ( ) ) ?;
162180
163- let res = compare_images ( file_a. path ( ) , file_b. path ( ) , 0.1 , false , & [ ] ) ?;
181+ let res = compare_images ( file_a. path ( ) , file_b. path ( ) , 0.1 , false , & [ ] , None ) ?;
164182 assert_eq ! ( res. diff_pixels, 0 ) ;
165183 assert_eq ! ( res. score, 1.0 ) ;
166184 assert ! ( res. ssim_score > 0.99 ) ;
@@ -181,14 +199,39 @@ mod tests {
181199 img_b. save ( file_b. path ( ) ) ?;
182200
183201 // Without ignore
184- let res1 = compare_images ( file_a. path ( ) , file_b. path ( ) , 0.1 , false , & [ ] ) ?;
202+ let res1 = compare_images ( file_a. path ( ) , file_b. path ( ) , 0.1 , false , & [ ] , None ) ?;
185203 assert_eq ! ( res1. diff_pixels, 1 ) ;
186204
187205 // With ignore
188206 let ignore = [ Region { x : 5 , y : 5 , width : 1 , height : 1 } ] ;
189- let res2 = compare_images ( file_a. path ( ) , file_b. path ( ) , 0.1 , false , & ignore) ?;
207+ let res2 = compare_images ( file_a. path ( ) , file_b. path ( ) , 0.1 , false , & ignore, None ) ?;
190208 assert_eq ! ( res2. diff_pixels, 0 ) ;
191209 assert_eq ! ( res2. score, 1.0 ) ;
192210 Ok ( ( ) )
193211 }
212+
213+ #[ test]
214+ fn test_compare_with_mask ( ) -> Result < ( ) > {
215+ let mut img_a: ImageBuffer < Rgba < u8 > , Vec < u8 > > = ImageBuffer :: new ( 10 , 10 ) ;
216+ for p in img_a. pixels_mut ( ) { * p = Rgba ( [ 100 , 100 , 100 , 255 ] ) ; }
217+
218+ let mut img_b = img_a. clone ( ) ;
219+ img_b. put_pixel ( 5 , 5 , Rgba ( [ 255 , 0 , 0 , 255 ] ) ) ;
220+
221+ let mut mask: ImageBuffer < Rgba < u8 > , Vec < u8 > > = ImageBuffer :: new ( 10 , 10 ) ;
222+ for p in mask. pixels_mut ( ) { * p = Rgba ( [ 255 , 255 , 255 , 255 ] ) ; }
223+ mask. put_pixel ( 5 , 5 , Rgba ( [ 0 , 0 , 0 , 255 ] ) ) ; // Mask out the difference
224+
225+ let file_a = tempfile:: Builder :: new ( ) . suffix ( ".png" ) . tempfile ( ) ?;
226+ let file_b = tempfile:: Builder :: new ( ) . suffix ( ".png" ) . tempfile ( ) ?;
227+ let file_mask = tempfile:: Builder :: new ( ) . suffix ( ".png" ) . tempfile ( ) ?;
228+
229+ img_a. save ( file_a. path ( ) ) ?;
230+ img_b. save ( file_b. path ( ) ) ?;
231+ mask. save ( file_mask. path ( ) ) ?;
232+
233+ let res = compare_images ( file_a. path ( ) , file_b. path ( ) , 0.1 , false , & [ ] , Some ( file_mask. path ( ) ) ) ?;
234+ assert_eq ! ( res. diff_pixels, 0 ) ;
235+ Ok ( ( ) )
236+ }
194237}
0 commit comments