stride3d/stride · error · ArgumentException

Block compression requires texture size to be a multiple of…

Error message

Block compression requires texture size to be a multiple of 4.

What it means

GetCompressedMono in OfflineRasterizedSpriteFontWriter block-compresses the rasterized monochromatic font texture into BC2 format. BC2 stores texels in 4x4 blocks, so both image dimensions must be multiples of 4; otherwise this ArgumentException is thrown. It is a hard layout requirement of block compression, not a configuration choice.

Solutions

  1. Adjust the font asset so the packed texture is 4-aligned: tweak FontSize, Spacing, or texture padding.
  2. If you control the writer, pad the bitmap up to a multiple of 4 before creating the BC2 image.
  3. Disable block compression for this font (use a non-BC texture format) if alignment keeps breaking.
  4. Log the generated bitmap dimensions to identify which setting breaks alignment.

Example fix

// before
var image = Graphics.Image.New2D(bitmap.Width, bitmap.Height, 1, Graphics.PixelFormat.BC2_UNorm);
// after
int w = (bitmap.Width + 3) & ~3, h = (bitmap.Height + 3) & ~3; // round up to multiple of 4
var image = Graphics.Image.New2D(w, h, 1, Graphics.PixelFormat.BC2_UNorm);
Defensive patterns

Strategy: validation

Validate before calling

// before requesting mono block compression, check dimensions
if ((bitmap.Width & 3) != 0 || (bitmap.Height & 3) != 0)
{
    bitmap.Mutate(ctx => ctx.Resize((bitmap.Width + 3) & ~3, (bitmap.Height + 3) & ~3)); // pad to multiple of 4
}

Type guard

bool IsBlockCompressible(System.Drawing.Size s) => (s.Width & 3) == 0 && (s.Height & 3) == 0;

Try / catch

try
{
    var compressed = GetCompressedMono(bitmap, options);
}
catch (ArgumentException ex) when (ex.Message.Contains("multiple of 4"))
{
    // fall back to an uncompressed pixel format for this font texture
    var fallback = Graphics.Image.New2D(bitmap.Width, bitmap.Height, 1, Graphics.PixelFormat.R8_UNorm);
}

Prevention

When it happens

Trigger: Compiling a rasterized sprite font whose generated packed bitmap has width or height not divisible by 4 (e.g. 1022x30) when the writer applies mono block compression — dimensions depend on glyph sizes, spacing, and the atlas packing result.

Common situations: Font metrics producing an odd-sized atlas; very small fonts or few glyphs yielding a narrow texture; changing FontSize/Spacing so the packed texture loses 4-pixel alignment.

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/27df364cdee56c43. Report an issue: GitHub.

Appendix: source

Thrown at sources/engine/Stride.Assets/SpriteFont/Compiler/OfflineRasterizedSpriteFontWriter.cs:149

            var pixelBuffer = image.PixelBuffer[0];
            for (int y = 0; y < bitmap.Height; y++)
            {
                for (int x = 0; x < bitmap.Width; x++)
                {
                    var color = bitmap[x, y];
                    pixelBuffer.SetPixel(x, y, new Core.Mathematics.Color(color.R, color.G, color.B, color.A));
                }
            }
            return image;
        }

        // Writes a block compressed monochromatic font texture.
        static unsafe Graphics.Image GetCompressedMono(Image<Rgba32> bitmap, SpriteFontAsset options)
        {
            if ((bitmap.Width & 3) != 0 ||
                (bitmap.Height & 3) != 0)
            {
                throw new ArgumentException("Block compression requires texture size to be a multiple of 4.");
            }

            var image = Graphics.Image.New2D(bitmap.Width, bitmap.Height, 1, Graphics.PixelFormat.BC2_UNorm);
            var pixelBuffer = (BC2Pixel*)image.PixelBuffer[0].DataPointer;
            for (int y = 0; y < bitmap.Height; y += 4)
            {
                for (int x = 0; x < bitmap.Width; x += 4)
                {
                    BC2Pixel bc2Pixel;
                    CompressBlock(bitmap, x, y, options, out bc2Pixel);
                    *pixelBuffer = bc2Pixel;
                    pixelBuffer++;
                }
            }
            return image;
        }

        [StructLayout(LayoutKind.Sequential, Pack = 4)]

View on GitHub (pinned to 96fad776d2)