@@ -221,6 +221,8 @@ pub(crate) fn box_kernel(_x: f32) -> f32 {
221221// ```new_width``` is the desired width of the new image 
222222// ```filter``` is the filter to use for sampling. 
223223// ```image``` is not necessarily Rgba and the order of channels is passed through. 
224+ // 
225+ // Note: if an empty image is passed in, panics unless the image is truly empty. 
224226fn  horizontal_sample < P ,  S > ( 
225227    image :  & Rgba32FImage , 
226228    new_width :  u32 , 
@@ -231,6 +233,13 @@ where
231233    S :  Primitive  + ' static , 
232234{ 
233235    let  ( width,  height)  = image. dimensions ( ) ; 
236+     // This is protection against a memory usage similar to #2340. See `vertical_sample`. 
237+     assert ! ( 
238+         // Checks the implication: (width == 0) -> (height == 0) 
239+         width != 0  || height == 0 , 
240+         "Unexpected prior allocation size. This case should have been handled by the caller" 
241+     ) ; 
242+ 
234243    let  mut  out = ImageBuffer :: new ( new_width,  height) ; 
235244    let  mut  ws = Vec :: new ( ) ; 
236245
@@ -463,13 +472,24 @@ pub fn interpolate_bilinear<P: Pixel>(
463472// ```filter``` is the filter to use for sampling. 
464473// The return value is not necessarily Rgba, the underlying order of channels in ```image``` is 
465474// preserved. 
475+ // 
476+ // Note: if an empty image is passed in, panics unless the image is truly empty. 
466477fn  vertical_sample < I ,  P ,  S > ( image :  & I ,  new_height :  u32 ,  filter :  & mut  Filter )  -> Rgba32FImage 
467478where 
468479    I :  GenericImageView < Pixel  = P > , 
469480    P :  Pixel < Subpixel  = S >  + ' static , 
470481    S :  Primitive  + ' static , 
471482{ 
472483    let  ( width,  height)  = image. dimensions ( ) ; 
484+ 
485+     // This is protection against a regression in memory usage such as #2340. Since the strategy to 
486+     // deal with it depends on the caller it is a precondition of this function. 
487+     assert ! ( 
488+         // Checks the implication: (height == 0) -> (width == 0) 
489+         height != 0  || width == 0 , 
490+         "Unexpected prior allocation size. This case should have been handled by the caller" 
491+     ) ; 
492+ 
473493    let  mut  out = ImageBuffer :: new ( width,  new_height) ; 
474494    let  mut  ws = Vec :: new ( ) ; 
475495
@@ -916,6 +936,16 @@ where
916936    I :: Pixel :  ' static , 
917937    <I :: Pixel  as  Pixel >:: Subpixel :  ' static , 
918938{ 
939+     // Check if there is nothing to sample from. 
940+     let  is_empty = { 
941+         let  ( width,  height)  = image. dimensions ( ) ; 
942+         width == 0  || height == 0 
943+     } ; 
944+ 
945+     if  is_empty { 
946+         return  ImageBuffer :: new ( nwidth,  nheight) ; 
947+     } 
948+ 
919949    // check if the new dimensions are the same as the old. if they are, make a copy instead of resampling 
920950    if  ( nwidth,  nheight)  == image. dimensions ( )  { 
921951        let  mut  tmp = ImageBuffer :: new ( image. width ( ) ,  image. height ( ) ) ; 
@@ -970,6 +1000,11 @@ where
9701000    } ; 
9711001
9721002    let  ( width,  height)  = image. dimensions ( ) ; 
1003+     let  is_empty = width == 0  || height == 0 ; 
1004+ 
1005+     if  is_empty { 
1006+         return  ImageBuffer :: new ( width,  height) ; 
1007+     } 
9731008
9741009    // Keep width and height the same for horizontal and 
9751010    // vertical sampling. 
@@ -1259,4 +1294,26 @@ mod tests {
12591294        let  result = resize ( & image,  22 ,  22 ,  FilterType :: Lanczos3 ) ; 
12601295        assert ! ( result. into_raw( ) . into_iter( ) . any( |c| c != 0 ) ) ; 
12611296    } 
1297+ 
1298+     #[ test]  
1299+     fn  issue_2340 ( )  { 
1300+         let  empty = crate :: GrayImage :: from_raw ( 1  << 31 ,  0 ,  vec ! [ ] ) . unwrap ( ) ; 
1301+         // Really we're checking that no overflow / outsized allocation happens here. 
1302+         let  result = resize ( & empty,  1 ,  1 ,  FilterType :: Lanczos3 ) ; 
1303+         assert ! ( result. into_raw( ) . into_iter( ) . all( |c| c == 0 ) ) ; 
1304+         // With the previous strategy before the regression this would allocate 1TB of memory for a 
1305+         // temporary during the sampling evaluation. 
1306+         let  result = resize ( & empty,  256 ,  256 ,  FilterType :: Lanczos3 ) ; 
1307+         assert ! ( result. into_raw( ) . into_iter( ) . all( |c| c == 0 ) ) ; 
1308+     } 
1309+ 
1310+     #[ test]  
1311+     fn  issue_2340_refl ( )  { 
1312+         // Tests the swapped coordinate version of `issue_2340`. 
1313+         let  empty = crate :: GrayImage :: from_raw ( 0 ,  1  << 31 ,  vec ! [ ] ) . unwrap ( ) ; 
1314+         let  result = resize ( & empty,  1 ,  1 ,  FilterType :: Lanczos3 ) ; 
1315+         assert ! ( result. into_raw( ) . into_iter( ) . all( |c| c == 0 ) ) ; 
1316+         let  result = resize ( & empty,  256 ,  256 ,  FilterType :: Lanczos3 ) ; 
1317+         assert ! ( result. into_raw( ) . into_iter( ) . all( |c| c == 0 ) ) ; 
1318+     } 
12621319} 
0 commit comments