AvaloniaUI/Avalonia · error · ArgumentOutOfRangeException
stride
Error message
stride
What it means
Bitmap.CopyPixelsCore computes the minimum stride for one row as ((Width * BitsPerPixel) + 7) / 8 and requires the caller-supplied stride to be at least that. A smaller stride would leave insufficient room per row and corrupt the row-by-row copy.
Source
Thrown at src/Avalonia.Base/Media/Imaging/Bitmap.cs:235
return sourceRect;
}
/// <summary>
/// Performs a row-by-row copy of pixels from a source buffer into a destination buffer.
/// </summary>
/// <remarks>
/// <paramref name="sourceRowBytes"/> is signed and may be negative: a negative value means the
/// source rows are laid out bottom-up, with <paramref name="sourceAddress"/> pointing at the
/// first (top) row. The destination <paramref name="stride"/> must be positive and at least the
/// tightly-packed row size. The caller is responsible for validating <paramref name="sourceRect"/>
/// against the source bounds (e.g. via <see cref="ValidateSourceRect"/>).
/// </remarks>
internal static unsafe void CopyPixelsCore(PixelRect sourceRect, IntPtr sourceAddress, int sourceRowBytes,
PixelFormat sourceFormat, IntPtr buffer, int bufferSize, int stride)
{
int minStride = checked(((sourceRect.Width * sourceFormat.BitsPerPixel) + 7) / 8);
if (stride < minStride)
throw new ArgumentOutOfRangeException(nameof(stride));
// 64-bit to avoid overflowing the guard for very large strides/heights, which would
// otherwise let an oversized contiguous blit/loop run past the buffers.
var minBufferSize = (long)stride * sourceRect.Height;
if (minBufferSize > bufferSize)
throw new ArgumentOutOfRangeException(nameof(bufferSize));
var offsetX = checked(((sourceRect.X * sourceFormat.BitsPerPixel) + 7) / 8);
// Fast-path: when the source and destination layouts are identical, tightly-packed and
// forward (no row padding, no X offset, positive stride), the whole region is contiguous in
// both buffers and can be copied with a single blit. This is meaningfully faster than the
// per-row loop (up to ~5x for small images, ~30% for large ones). Requiring stride == minStride
// also guarantees we don't read past the source's last row.
if (offsetX == 0 && sourceRowBytes == stride && stride == minStride)
{
Unsafe.CopyBlock(buffer.ToPointer(),
(sourceAddress + sourceRowBytes * sourceRect.Y).ToPointer(), (uint)minBufferSize);View on GitHub (pinned to 11c5427268)
Solutions
- Always compute stride from the format: int minStride = (sourceRect.Width * format.BitsPerPixel + 7) / 8; int stride = minStride; // tight, or padded.
- Use 4-byte alignment (stride = ((minStride + 3) / 4) * 4) when the destination expects row padding.
- Verify the PixelFormat used in the math matches the actual bitmap.
- Add a debug assertion: Debug.Assert(stride >= minStride).
Example fix
// before int stride = sourceRect.Width; // wrong unit bmp.CopyPixels(rect, buffer, bufferSize, stride); // after int bitsPerPixel = format.BitsPerPixel; int stride = (sourceRect.Width * bitsPerPixel + 7) / 8; bmp.CopyPixels(rect, buffer, bufferSize, stride);
Defensive patterns
Strategy: validation
Validate before calling
int bitsPerPixel = format.BitsPerPixel; int minStride = (sourceRect.Width * bitsPerPixel + 7) / 8; int stride = Math.Max(minStride, requestedStride); // never below minStride
Prevention
- Always derive stride from BitsPerPixel, not from pixel count.
- Update the stride formula when changing PixelFormat.
- Add Debug.Assert(stride >= minStride) before CopyPixels.
When it happens
Trigger: Passing stride = sourceRect.Width (counting pixels, not bytes); forgetting to multiply by bytes-per-pixel for formats like Bgra32 (4 bytes); using a stride sized for a different pixel format.
Common situations: Computing stride as width instead of width*bytesPerPixel; switching the bitmap's PixelFormat (e.g. Bgra8888 -> Bgr24) without updating the stride formula; copying into a tightly packed buffer where stride is computed correctly but BitsPerPixel is mis-assumed.
Related errors
AI-assisted analysis of AvaloniaUI/Avalonia@11c5427268 (2026-08-13).
Data as JSON: /api/errors/14a10695e887a942.
Report an issue: GitHub.