AvaloniaUI/Avalonia · error · ArgumentOutOfRangeException

stride

Error message

stride

What it means

The WriteableBitmap(IntPtr data) constructor computes a minimum per-row stride = (format.BitsPerPixel * width + 7) / 8 and throws ArgumentOutOfRangeException(nameof(stride)) when the supplied stride is smaller than that minimum. The stride must accommodate at least one full row of source pixels so the per-row Unsafe.CopyBlock copies valid data.

Source

Thrown at src/Avalonia.Base/Media/Imaging/WriteableBitmap.cs:55

            _pixelFormatMemory = pixelFormatMemory;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="WriteableBitmap"/> class with existing pixel data
        /// The data is copied to the bitmap
        /// </summary>
        /// <param name="format">The pixel format.</param>
        /// <param name="alphaFormat">The alpha format.</param>
        /// <param name="data">The pointer to the source bytes.</param>
        /// <param name="size">The size of the bitmap in device pixels.</param>
        /// <param name="dpi">The DPI of the bitmap.</param>
        /// <param name="stride">The number of bytes per row.</param>
        public unsafe WriteableBitmap(PixelFormat format, AlphaFormat alphaFormat, IntPtr data, PixelSize size, Vector dpi, int stride)
            : this(size, dpi, format, alphaFormat)
        {
            var minStride = (format.BitsPerPixel * size.Width + 7) / 8;
            if (minStride > stride)
                throw new ArgumentOutOfRangeException(nameof(stride));

            using (var locked = Lock())
            {
                for (var y = 0; y < size.Height; y++)
                    Unsafe.CopyBlock((locked.Address + locked.RowBytes * y).ToPointer(),
                        (data + y * stride).ToPointer(), (uint)minStride);
            }
        }

        public override PixelFormat? Format => _pixelFormatMemory?.Format ?? base.Format;
        
        public ILockedFramebuffer Lock()
        {
            if (_pixelFormatMemory == null)
                return ((IWriteableBitmapImpl)PlatformImpl.Item).Lock();
            
            return new LockedFramebuffer(_pixelFormatMemory.Address, _pixelFormatMemory.Size,
                _pixelFormatMemory.RowBytes,

View on GitHub (pinned to 11c5427268)

Solutions

  1. Compute stride from the format: stride = (format.BitsPerPixel * size.Width + 7) / 8, or larger.
  2. If the source buffer uses extra padding per row, pass the actual source row pitch and let Avalonia copy minStride bytes per row.
  3. Validate stride >= minStride before constructing.

Example fix

// before
int stride = size.Width; // wrong for 32-bpp
new WriteableBitmap(PixelFormat.Bgra8888, AlphaFormat.Premultiplied, data, size, dpi, stride);

// after
int stride = (PixelFormat.Bgra8888.BitsPerPixel * size.Width + 7) / 8; // 4*width
new WriteableBitmap(PixelFormat.Bgra8888, AlphaFormat.Premultiplied, data, size, dpi, stride);
Defensive patterns

Strategy: validation

Validate before calling

int minStride = (format.BitsPerPixel * size.Width + 7) / 8;
if (stride < minStride)
    throw new ArgumentOutOfRangeException(nameof(stride), $"stride must be >= {minStride}.");
var wb = new WriteableBitmap(format, alphaFormat, data, size, dpi, stride);

Prevention

When it happens

Trigger: Calling new WriteableBitmap(format, alphaFormat, data, size, dpi, stride) with a stride smaller than bitsPerPixel*width rounded up to bytes — e.g. passing width in bytes instead of the row pitch, or miscomputing bitsPerPixel for packed formats like BlackWhite/Gray2.

Common situations: Assuming stride = width (bytes) for a 32-bpp format; using width*sizeof(int) for a 16-bpp format; interop with a source buffer whose real pitch is larger but the caller passed the wrong value.

Related errors


AI-assisted analysis of AvaloniaUI/Avalonia@11c5427268 (2026-08-13). Data as JSON: /api/errors/6c820b37f3628bcc. Report an issue: GitHub.