{"record":{"id":"27df364cdee56c43","repo":"stride3d/stride","slug":"block-compression-requires-texture-size-to-be-a-multiple-of","errorCode":null,"errorMessage":"Block compression requires texture size to be a multiple of 4.","messagePattern":"Block compression requires texture size to be a multiple of 4\\.","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"sources/engine/Stride.Assets/SpriteFont/Compiler/OfflineRasterizedSpriteFontWriter.cs","lineNumber":149,"sourceCode":"            var pixelBuffer = image.PixelBuffer[0];\n            for (int y = 0; y < bitmap.Height; y++)\n            {\n                for (int x = 0; x < bitmap.Width; x++)\n                {\n                    var color = bitmap[x, y];\n                    pixelBuffer.SetPixel(x, y, new Core.Mathematics.Color(color.R, color.G, color.B, color.A));\n                }\n            }\n            return image;\n        }\n\n        // Writes a block compressed monochromatic font texture.\n        static unsafe Graphics.Image GetCompressedMono(Image<Rgba32> bitmap, SpriteFontAsset options)\n        {\n            if ((bitmap.Width & 3) != 0 ||\n                (bitmap.Height & 3) != 0)\n            {\n                throw new ArgumentException(\"Block compression requires texture size to be a multiple of 4.\");\n            }\n\n            var image = Graphics.Image.New2D(bitmap.Width, bitmap.Height, 1, Graphics.PixelFormat.BC2_UNorm);\n            var pixelBuffer = (BC2Pixel*)image.PixelBuffer[0].DataPointer;\n            for (int y = 0; y < bitmap.Height; y += 4)\n            {\n                for (int x = 0; x < bitmap.Width; x += 4)\n                {\n                    BC2Pixel bc2Pixel;\n                    CompressBlock(bitmap, x, y, options, out bc2Pixel);\n                    *pixelBuffer = bc2Pixel;\n                    pixelBuffer++;\n                }\n            }\n            return image;\n        }\n\n        [StructLayout(LayoutKind.Sequential, Pack = 4)]","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/engine/Stride.Assets/SpriteFont/Compiler/OfflineRasterizedSpriteFontWriter.cs#L131-L167","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nvar image = Graphics.Image.New2D(bitmap.Width, bitmap.Height, 1, Graphics.PixelFormat.BC2_UNorm);\n// after\nint w = (bitmap.Width + 3) & ~3, h = (bitmap.Height + 3) & ~3; // round up to multiple of 4\nvar image = Graphics.Image.New2D(w, h, 1, Graphics.PixelFormat.BC2_UNorm);","handlingStrategy":"validation","validationCode":"// before requesting mono block compression, check dimensions\nif ((bitmap.Width & 3) != 0 || (bitmap.Height & 3) != 0)\n{\n    bitmap.Mutate(ctx => ctx.Resize((bitmap.Width + 3) & ~3, (bitmap.Height + 3) & ~3)); // pad to multiple of 4\n}","typeGuard":"bool IsBlockCompressible(System.Drawing.Size s) => (s.Width & 3) == 0 && (s.Height & 3) == 0;","tryCatchPattern":"try\n{\n    var compressed = GetCompressedMono(bitmap, options);\n}\ncatch (ArgumentException ex) when (ex.Message.Contains(\"multiple of 4\"))\n{\n    // fall back to an uncompressed pixel format for this font texture\n    var fallback = Graphics.Image.New2D(bitmap.Width, bitmap.Height, 1, Graphics.PixelFormat.R8_UNorm);\n}","preventionTips":["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."],"tags":["textures","compression","fonts"],"backgroundTag":"invalid-argument-value","analyzedSha":"96fad776d210c221682aac1ccdf4c79dc046fc38","analyzedAt":"2026-09-14T02:59:31.279Z","contentChangedAt":"2026-09-14T02:59:31.279Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}