diff --git a/src/Avalonia.Base/Media/Imaging/Bitmap.cs b/src/Avalonia.Base/Media/Imaging/Bitmap.cs index fc6dfb1f19..2a1ce15feb 100644 --- a/src/Avalonia.Base/Media/Imaging/Bitmap.cs +++ b/src/Avalonia.Base/Media/Imaging/Bitmap.cs @@ -182,12 +182,15 @@ namespace Avalonia.Media.Imaging private protected unsafe void CopyPixelsCore(PixelRect sourceRect, IntPtr buffer, int bufferSize, int stride, ILockedFramebuffer fb) { + if (Format == null) + throw new NotSupportedException("CopyPixels is not supported for this bitmap type"); + if ((sourceRect.Width <= 0 || sourceRect.Height <= 0) && (sourceRect.X != 0 || sourceRect.Y != 0)) throw new ArgumentOutOfRangeException(nameof(sourceRect)); if (sourceRect.X < 0 || sourceRect.Y < 0) throw new ArgumentOutOfRangeException(nameof(sourceRect)); - + if (sourceRect.Width <= 0) sourceRect = sourceRect.WithWidth(PixelSize.Width); if (sourceRect.Height <= 0) @@ -195,7 +198,7 @@ namespace Avalonia.Media.Imaging if (sourceRect.Right > PixelSize.Width || sourceRect.Bottom > PixelSize.Height) throw new ArgumentOutOfRangeException(nameof(sourceRect)); - + int minStride = checked(((sourceRect.Width * fb.Format.BitsPerPixel) + 7) / 8); if (stride < minStride) throw new ArgumentOutOfRangeException(nameof(stride)); @@ -204,14 +207,16 @@ namespace Avalonia.Media.Imaging if (minBufferSize > bufferSize) throw new ArgumentOutOfRangeException(nameof(bufferSize)); + var offsetX = checked(((sourceRect.X * Format.Value.BitsPerPixel) + 7) / 8); + for (var y = 0; y < sourceRect.Height; y++) { - var srcAddress = fb.Address + fb.RowBytes * y; + var srcAddress = fb.Address + fb.RowBytes * (sourceRect.Y + y) + offsetX; var dstAddress = buffer + stride * y; Unsafe.CopyBlock(dstAddress.ToPointer(), srcAddress.ToPointer(), (uint)minStride); } } - + public virtual void CopyPixels(PixelRect sourceRect, IntPtr buffer, int bufferSize, int stride) { if ( diff --git a/tests/Avalonia.RenderTests/Media/BitmapTests.cs b/tests/Avalonia.RenderTests/Media/BitmapTests.cs index 6ddeda1b5d..38b23e1bf7 100644 --- a/tests/Avalonia.RenderTests/Media/BitmapTests.cs +++ b/tests/Avalonia.RenderTests/Media/BitmapTests.cs @@ -246,5 +246,34 @@ namespace Avalonia.Direct2D1.RenderTests.Media bmp.CopyPixels(default, new IntPtr(pCopyTo), data.Length, stride); Assert.Equal(data, copyTo); } + + [Fact] + public unsafe void Should_CopyPixels_With_Source_Rect() + { + var size = 80; + var partSize = 20; + var bitmap = new RenderTargetBitmap(new PixelSize(size, size)); + + using (var context = bitmap.CreateDrawingContext()) + { + context.FillRectangle(Brushes.Black, + new Rect(0, 0, bitmap.PixelSize.Width, bitmap.PixelSize.Height)); + context.FillRectangle(Brushes.White, new Rect(partSize, partSize, partSize, partSize)); + } + + var bpp = bitmap.Format!.Value.BitsPerPixel / 8; + var buffer = new byte[partSize * partSize * bpp]; + + fixed (byte* pointer = buffer) + { + bitmap.CopyPixels(new PixelRect(partSize, partSize, partSize, partSize), (IntPtr)pointer, + buffer.Length, partSize * bpp); + } + + foreach (var t in buffer) + { + Assert.Equal(byte.MaxValue, t); + } + } } }