stride3d/stride · error · ArgumentException

Should be a multiple of 4.

Error message

Should be a multiple of 4.

What it means

CopyMemoryBGRA performs the RGBA-to-BGRA channel swap by iterating the buffer as uint (4 bytes per pixel), so the byte count must be a multiple of 4. It validates sizeInBytesToCopy & 3 == 0 and throws ArgumentException otherwise, preventing a partial-pixel copy or out-of-bounds write.

Solutions

  1. Round sizeInBytesToCopy to a multiple of 4 (only copy whole pixels).
  2. Copy row by row with width*4 bytes per row instead of one big bulk copy.
  3. Convert 3-byte formats to a 4-byte format before BGRA swapping.

Example fix

// before
CopyMemoryBGRA(dest, src, width * 3);
// after
CopyMemoryBGRA(dest, src, width * 4); // 4 bytes per pixel
Defensive patterns

Strategy: validation

Validate before calling

if ((sizeInBytesToCopy & 3) != 0)
    throw new ArgumentException("CopyMemoryBGRA requires size to be a multiple of 4");

Type guard

bool IsWholePixelCount(int bytes) => (bytes & 3) == 0;

Try / catch

try { CopyMemoryBGRA(dest, src, size); }
catch (ArgumentException ex) when (ex.ParamName == "sizeInBytesToCopy") { CopyRowByRow(dest, src, size); }

Prevention

When it happens

Trigger: Invoking CopyMemoryBGRA(dest, src, sizeInBytesToCopy) with a size not divisible by 4 — e.g. copying a partial row of an odd-width 24bpp buffer, or passing BufferStride minus padding incorrectly.

Common situations: Copying cropped sub-regions whose width is not an exact number of pixels in 4-byte units; buffers from 3-byte-per-pixel formats; stride arithmetic that leaves trailing padding bytes out of the size.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/078e58cfb39abc94. Report an issue: GitHub.

Appendix: source

Thrown at sources/engine/Stride.Foundation/Graphics/StandardImageHelper.cs:131

    /// <summary>
    ///   Copies a block of memory from a source buffer to a destination buffer,
    ///   converting each 32-bit pixel from RGBA to BGRA format.
    /// </summary>
    /// <param name="dest">A pointer to the destination buffer that will receive the converted BGRA pixel data.</param>
    /// <param name="src">A pointer to the source buffer containing the RGBA pixel data to copy and convert.</param>
    /// <param name="sizeInBytesToCopy">
    ///   The number of bytes to copy and convert. Must be a multiple of 4, as each pixel is represented by 4 bytes.
    /// </param>
    /// <exception cref="ArgumentException"><paramref name="sizeInBytesToCopy"/> is not a multiple of 4.</exception>
    /// <remarks>
    ///   The conversion swaps the red and blue channels for each pixel, effectively transforming the format
    ///   from RGBA to BGRA.
    /// </remarks>
    private static unsafe void CopyMemoryBGRA(IntPtr dest, IntPtr src, int sizeInBytesToCopy)
    {
        if ((sizeInBytesToCopy & 3) != 0)
            throw new ArgumentException("Should be a multiple of 4.", nameof(sizeInBytesToCopy));

        var bufferSize = sizeInBytesToCopy / 4;
        var srcPtr = (uint*) src;
        var destPtr = (uint*) dest;
        for (int i = 0; i < bufferSize; ++i)
        {
            var value = *srcPtr++;
            // value: 0xAARRGGBB or in reverse 0xAABBGGRR
            value = BinaryPrimitives.ReverseEndianness(value);
            // value: 0xBBGGRRAA or in reverse 0xRRGGBBAA
            value = BitOperations.RotateRight(value, 8);
            // value: 0xAABBGGRR or in reverse 0xAARRGGBB
            *destPtr++ = value;
        }
    }

    /// <summary>
    ///   Copies a block of memory from a source buffer to a destination buffer,

View on GitHub (pinned to 96fad776d2)