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
- Adjust the font asset so the packed texture is 4-aligned: tweak FontSize, Spacing, or texture padding.
- If you control the writer, pad the bitmap up to a multiple of 4 before creating the BC2 image.
- Disable block compression for this font (use a non-BC texture format) if alignment keeps breaking.
- 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
- Prefer font sizes/spacings that keep the packed atlas dimensions 4-aligned.
- Pad font textures up to multiples of 4 before BC compression in custom writers.
- For very small fonts, disable block compression instead of forcing BC2.
- Unit-test the writer with minimal glyph sets, which most easily produce odd atlas sizes.
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
- Count cannot be less than zero
- Unexpected end of stream
- Can't read beyond end of stream.
- Chunks with multiple passes are not supported.
- Font file not found
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)