@@ -10,7 +10,7 @@ use crate::color::{FromColor, FromPrimitive, Luma, LumaA, Rgb, Rgba};
1010use crate :: error:: {
1111 ImageResult , ParameterError , ParameterErrorKind , UnsupportedError , UnsupportedErrorKind ,
1212} ;
13- use crate :: flat:: { FlatSamples , SampleLayout } ;
13+ use crate :: flat:: { FlatSamples , SampleLayout , ViewOfPixel } ;
1414use crate :: math:: Rect ;
1515use crate :: metadata:: cicp:: { CicpApplicable , CicpPixelCast , CicpRgb , ColorComponentForCicp } ;
1616use crate :: traits:: { EncodableLayout , Pixel , PixelWithColorType } ;
@@ -1322,6 +1322,16 @@ where
13221322 * self . get_pixel ( x, y)
13231323 }
13241324
1325+ fn to_pixel_view ( & self ) -> Option < ViewOfPixel < ' _ , Self :: Pixel > > {
1326+ let samples = FlatSamples {
1327+ samples : & * self . data ,
1328+ layout : self . sample_layout ( ) ,
1329+ color_hint : None ,
1330+ } ;
1331+
1332+ samples. into_view ( ) . ok ( )
1333+ }
1334+
13251335 /// Returns the pixel located at (x, y), ignoring bounds checking.
13261336 #[ inline( always) ]
13271337 unsafe fn unsafe_get_pixel ( & self , x : u32 , y : u32 ) -> P {
@@ -1364,6 +1374,61 @@ where
13641374 self . get_pixel_mut ( x, y) . blend ( & p) ;
13651375 }
13661376
1377+ fn copy_from_samples (
1378+ & mut self ,
1379+ view : ViewOfPixel < ' _ , Self :: Pixel > ,
1380+ x : u32 ,
1381+ y : u32 ,
1382+ ) -> ImageResult < ( ) > {
1383+ let ( width, height) = view. dimensions ( ) ;
1384+ let pix_stride = usize:: from ( <Self :: Pixel as Pixel >:: CHANNEL_COUNT ) ;
1385+ Rect :: from_image_at ( & view, x, y) . test_in_bounds ( self ) ?;
1386+
1387+ if width == 0 || height == 0 || pix_stride == 0 {
1388+ return Ok ( ( ) ) ;
1389+ }
1390+
1391+ // Since this image is not empty, all its indices fit into `usize` as they address the
1392+ // memory resident buffer of `self`.
1393+ let row_len = width as usize * pix_stride;
1394+ let img_sh = self . width as usize ;
1395+
1396+ let ( sw, sh) = view. strides_wh ( ) ;
1397+ let view_samples: & [ _ ] = view. samples ( ) ;
1398+ let inner = self . inner_pixels_mut ( ) ;
1399+
1400+ let img_pixel_indices_unchecked =
1401+ |x : u32 , y : u32 | ( y as usize * img_sh + x as usize ) * pix_stride;
1402+
1403+ // Can we use row-by-row byte copy?
1404+ if sw == pix_stride {
1405+ for j in 0 ..height {
1406+ let start = img_pixel_indices_unchecked ( x, j + y) ;
1407+ let img_row = & mut inner[ start..] [ ..row_len] ;
1408+ let view_row = & view_samples[ j as usize * sh..] [ ..row_len] ;
1409+ img_row. copy_from_slice ( view_row) ;
1410+ }
1411+
1412+ return Ok ( ( ) ) ;
1413+ }
1414+
1415+ // Fallback behavior.
1416+ for j in 0 ..height {
1417+ let img_start = img_pixel_indices_unchecked ( x, j + y) ;
1418+ let img_row = & mut inner[ img_start..] [ ..row_len] ;
1419+ let pixels = img_row. chunks_exact_mut ( pix_stride) ;
1420+
1421+ let view_start = j as usize * sh;
1422+
1423+ for ( i, sp) in pixels. enumerate ( ) {
1424+ let view_pixel = & view_samples[ i * sw + view_start..] [ ..pix_stride] ;
1425+ sp. copy_from_slice ( view_pixel) ;
1426+ }
1427+ }
1428+
1429+ Ok ( ( ) )
1430+ }
1431+
13671432 fn copy_within ( & mut self , source : Rect , x : u32 , y : u32 ) -> bool {
13681433 let Rect {
13691434 x : sx,
@@ -1928,8 +1993,8 @@ mod test {
19281993 use crate :: metadata:: CicpMatrixCoefficients ;
19291994 use crate :: metadata:: CicpTransform ;
19301995 use crate :: metadata:: CicpVideoFullRangeFlag ;
1931- use crate :: GenericImage as _;
19321996 use crate :: ImageFormat ;
1997+ use crate :: { GenericImage as _, GenericImageView as _} ;
19331998 use crate :: { Luma , LumaA , Pixel , Rgb , Rgba } ;
19341999 use num_traits:: Zero ;
19352000
@@ -2367,6 +2432,160 @@ mod test {
23672432 "ImageBuffer::<Rgba<f32>, _> { width: 16, height: 16, color: CicpRgb { primaries: Rgb240m, transfer: LogSqrt, luminance: NonConstant } }"
23682433 ) ;
23692434 }
2435+
2436+ /// We specialize copy_from on types that provide `as_samples` so test that.
2437+ #[ test]
2438+ fn copy_from_subimage_to_middle ( ) {
2439+ let mut source = RgbImage :: new ( 16 , 16 ) ;
2440+ let mut target = RgbImage :: new ( 16 , 16 ) ;
2441+
2442+ source. put_pixel ( 8 , 8 , Rgb ( [ 255 , 8 , 8 ] ) ) ;
2443+ source. put_pixel ( 9 , 8 , Rgb ( [ 255 , 9 , 8 ] ) ) ;
2444+ source. put_pixel ( 9 , 9 , Rgb ( [ 255 , 9 , 9 ] ) ) ;
2445+
2446+ let view = source. view ( Rect :: from_xy_ranges ( 8 ..10 , 8 ..10 ) ) ;
2447+ assert ! ( target. copy_from( & * view, 4 , 4 ) . is_ok( ) ) ;
2448+
2449+ // Check the pixel was copied.
2450+ assert_eq ! ( * target. get_pixel( 4 , 4 ) , Rgb ( [ 255 , 8 , 8 ] ) ) ;
2451+ assert_eq ! ( * target. get_pixel( 5 , 4 ) , Rgb ( [ 255 , 9 , 8 ] ) ) ;
2452+ assert_eq ! ( * target. get_pixel( 5 , 5 ) , Rgb ( [ 255 , 9 , 9 ] ) ) ;
2453+
2454+ // Check that were the only copied pixel.
2455+ assert_eq ! (
2456+ target. iter( ) . copied( ) . map( usize :: from) . sum:: <usize >( ) ,
2457+ 3 * ( 255 + 8 + 9 )
2458+ ) ;
2459+ }
2460+
2461+ #[ test]
2462+ fn copy_from_band ( ) {
2463+ let source = RgbImage :: from_fn ( 16 , 8 , |x, y| Rgb ( [ x as u8 , y as u8 , 0 ] ) ) ;
2464+ let mut target = RgbImage :: new ( 16 , 16 ) ;
2465+
2466+ assert ! ( target. copy_from( & source, 0 , 4 ) . is_ok( ) ) ;
2467+
2468+ let lhs = source. chunks_exact ( 48 ) ;
2469+ let rhs = target. chunks_exact ( 48 ) . skip ( 4 ) . take ( 8 ) ;
2470+
2471+ assert ! ( lhs. eq( rhs) ) ;
2472+ }
2473+
2474+ #[ test]
2475+ fn copy_from_pixel ( ) {
2476+ let bg = Rgb ( [ 255 , 0 , 128 ] ) ;
2477+ let samples = crate :: flat:: FlatSamples :: with_monocolor ( & bg, 4 , 4 ) ;
2478+ let source = samples. as_view ( ) . unwrap ( ) ;
2479+
2480+ let mut target = RgbImage :: new ( 16 , 16 ) ;
2481+ assert ! ( target. copy_from( & source, 4 , 4 ) . is_ok( ) ) ;
2482+
2483+ for i in 4 ..8 {
2484+ for j in 4 ..8 {
2485+ assert_eq ! ( * target. get_pixel( i, j) , bg) ;
2486+ }
2487+ }
2488+
2489+ assert_eq ! (
2490+ target. iter( ) . copied( ) . map( usize :: from) . sum:: <usize >( ) ,
2491+ 16 * ( 255 + 128 )
2492+ ) ;
2493+ }
2494+
2495+ #[ test]
2496+ fn copy_from_strided ( ) {
2497+ #[ rustfmt:: skip]
2498+ let sample_data = [
2499+ 1 , 0xff , 0 , 0 , 2 , 0xff ,
2500+ 3 , 0xff , 0 , 0 , 4 , 0xff
2501+ ] ;
2502+
2503+ let samples = crate :: flat:: FlatSamples {
2504+ samples : & sample_data,
2505+ layout : crate :: flat:: SampleLayout {
2506+ channels : 2 ,
2507+ channel_stride : 1 ,
2508+ width : 2 ,
2509+ width_stride : 4 ,
2510+ height : 2 ,
2511+ height_stride : 6 ,
2512+ } ,
2513+ color_hint : None ,
2514+ } ;
2515+
2516+ let source = samples. as_view :: < LumaA < u8 > > ( ) . unwrap ( ) ;
2517+ let mut target = crate :: GrayAlphaImage :: new ( 16 , 16 ) ;
2518+ assert ! ( target. copy_from( & source, 4 , 4 ) . is_ok( ) ) ;
2519+
2520+ assert_eq ! ( * target. get_pixel( 4 , 4 ) , LumaA ( [ 1 , 0xff ] ) ) ;
2521+ assert_eq ! ( * target. get_pixel( 5 , 4 ) , LumaA ( [ 2 , 0xff ] ) ) ;
2522+ assert_eq ! ( * target. get_pixel( 4 , 5 ) , LumaA ( [ 3 , 0xff ] ) ) ;
2523+ assert_eq ! ( * target. get_pixel( 5 , 5 ) , LumaA ( [ 4 , 0xff ] ) ) ;
2524+
2525+ assert_eq ! (
2526+ target. iter( ) . copied( ) . map( usize :: from) . sum:: <usize >( ) ,
2527+ sample_data. iter( ) . copied( ) . map( usize :: from) . sum:: <usize >( ) ,
2528+ ) ;
2529+ }
2530+
2531+ #[ test]
2532+ fn copy_from_strided_subimage ( ) {
2533+ #[ rustfmt:: skip]
2534+ let sample_data = [
2535+ 1 , 0xff , 0 , 0 , 2 , 0xff ,
2536+ 3 , 0xff , 0 , 0 , 4 , 0xff
2537+ ] ;
2538+
2539+ let samples = crate :: flat:: FlatSamples {
2540+ samples : & sample_data,
2541+ layout : crate :: flat:: SampleLayout {
2542+ channels : 2 ,
2543+ channel_stride : 1 ,
2544+ width : 2 ,
2545+ width_stride : 4 ,
2546+ height : 2 ,
2547+ height_stride : 6 ,
2548+ } ,
2549+ color_hint : None ,
2550+ } ;
2551+
2552+ let view = samples. as_view :: < LumaA < u8 > > ( ) . unwrap ( ) ;
2553+ let source = view. view ( Rect :: from_xy_ranges ( 1 ..2 , 0 ..2 ) ) ;
2554+
2555+ let mut target = crate :: GrayAlphaImage :: new ( 16 , 16 ) ;
2556+ assert ! ( target. copy_from( & * source, 4 , 4 ) . is_ok( ) ) ;
2557+
2558+ assert_eq ! ( * target. get_pixel( 4 , 4 ) , LumaA ( [ 2 , 0xff ] ) ) ;
2559+ assert_eq ! ( * target. get_pixel( 4 , 5 ) , LumaA ( [ 4 , 0xff ] ) ) ;
2560+
2561+ assert_eq ! (
2562+ target. iter( ) . copied( ) . map( usize :: from) . sum:: <usize >( ) ,
2563+ 2usize + 0xff + 4 + 0xff
2564+ ) ;
2565+ }
2566+
2567+ #[ test]
2568+ fn copy_from_subimage_subimage ( ) {
2569+ let mut source = RgbImage :: new ( 16 , 16 ) ;
2570+ let mut target = RgbImage :: new ( 16 , 16 ) ;
2571+
2572+ source. put_pixel ( 8 , 8 , Rgb ( [ 255 , 8 , 8 ] ) ) ;
2573+ source. put_pixel ( 9 , 8 , Rgb ( [ 255 , 9 , 8 ] ) ) ;
2574+ source. put_pixel ( 9 , 9 , Rgb ( [ 255 , 9 , 9 ] ) ) ;
2575+
2576+ let view = source. view ( Rect :: from_xy_ranges ( 8 ..10 , 8 ..10 ) ) ;
2577+ let view = view. view ( Rect :: from_xy_ranges ( 1 ..2 , 0 ..1 ) ) ;
2578+ assert ! ( target. copy_from( & * view, 4 , 4 ) . is_ok( ) ) ;
2579+
2580+ // Check the pixel was copied.
2581+ assert_eq ! ( * target. get_pixel( 4 , 4 ) , Rgb ( [ 255 , 9 , 8 ] ) ) ;
2582+
2583+ // Check that was the only copied pixel.
2584+ assert_eq ! (
2585+ target. iter( ) . copied( ) . map( usize :: from) . sum:: <usize >( ) ,
2586+ 255 + 9 + 8
2587+ ) ;
2588+ }
23702589}
23712590
23722591#[ cfg( test) ]
0 commit comments