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

  1. Always compute stride from the format: int minStride = (sourceRect.Width * format.BitsPerPixel + 7) / 8; int stride = minStride; // tight, or padded.
  2. Use 4-byte alignment (stride = ((minStride + 3) / 4) * 4) when the destination expects row padding.
  3. Verify the PixelFormat used in the math matches the actual bitmap.
  4. 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

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.