HMCL-dev/HMCL · error · PngIntegrityException
Invalid greyscale bit-depth
Error message
Invalid greyscale bit-depth: %d
What it means
PngIntegrityException thrown when a greyscale (colour type 0) PNG declares a bit depth that is not one of the PNG-legal values 1, 2, 4, 8, or 16 (e.g. 0, 3, 5, 32). The processor factory has no case for such a depth, so it rejects the header as corrupt. The TODO notes this check ideally belongs in header parsing.
Solutions
- Validate the IHDR bit depth (1/2/4/8/16 for greyscale) before constructing processors — ideally at header parse time as the TODO suggests
- Reject or quarantine the file as structurally invalid
- Re-obtain the image from a trusted source if corruption is suspected
- Catch PngIntegrityException and surface a clear 'corrupt PNG header' error to the user
Example fix
// before: trusting the parsed header
Argb8888ScanlineProcessor p = Argb8888Processors.from(header, bitmap); // throws for bitDepth=3
// after: validate early
if (header.colourType == PngColourType.PNG_GREYSCALE
&& (header.bitDepth != 1 && header.bitDepth != 2 && header.bitDepth != 4
&& header.bitDepth != 8 && header.bitDepth != 16)) {
throw new PngIntegrityException("IHDR bit depth invalid for greyscale: " + header.bitDepth);
}
Argb8888ScanlineProcessor p = Argb8888Processors.from(header, bitmap); Defensive patterns
Strategy: validation
Validate before calling
if (header.colourType == PngColourType.PNG_GREYSCALE) {
int d = header.bitDepth;
if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16) {
throw new IllegalArgumentException("Invalid greyscale bit depth: " + d);
}
} Try / catch
try {
processor = Argb8888Processors.from(header, bitmap);
} catch (PngIntegrityException e) {
throw new IOException("Corrupt PNG IHDR: " + e.getMessage(), e);
} Prevention
- Validate the full IHDR (width, height, depth, colour type, interlace) at parse time
- Fuzz-test your PNG ingestion path
- Checksum-verify downloads before decoding
- Reject files whose header fields violate the PNG spec table
When it happens
Trigger: Calling Argb8888Processors.from with a header of colourType PNG_GREYSCALE and bitDepth outside {1,2,4,8,16} — reached via the switch's default branch.
Common situations: Corrupt or fuzzed PNG files with tampered IHDR bytes; hand-crafted binary PNGs in tests; memory/bit corruption during file transfer; buggy custom encoders writing an invalid depth byte.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- Invalid greyscale-with-alpha bit-depth
- Invalid truecolour bit-depth
- Invalid indexed colour bit-depth
- Invalid truecolour with alpha bit-depth
- Valid greyscale bit depths are 1, 2, 4, 8, not
AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10).
Data as JSON: /api/errors/01d1caa1b14e5bcc.
Report an issue: GitHub.
Appendix: source
Thrown at HMCL/src/main/java/org/jackhuang/hmcl/ui/image/apng/argb8888/Argb8888Processors.java:46
*/
public static Argb8888ScanlineProcessor from(PngHeader header, PngScanlineBuffer scanlineReader, Argb8888Bitmap bitmap) throws PngException {
int bytesPerScanline = header.bytesPerRow;
switch (header.colourType) {
case PNG_GREYSCALE:
switch (header.bitDepth) {
case 1:
return new IndexedColourBits(bytesPerScanline, bitmap, 7, 0x01, PngConstants.SHIFTS_1, Argb8888Palette.forGreyscale(1));
case 2:
return new IndexedColourBits(bytesPerScanline, bitmap, 3, 0x03, PngConstants.SHIFTS_2, Argb8888Palette.forGreyscale(2));
case 4:
return new IndexedColourBits(bytesPerScanline, bitmap, 1, 0x0F, PngConstants.SHIFTS_4, Argb8888Palette.forGreyscale(4));
case 8:
return new Greyscale8(bytesPerScanline, bitmap);
case 16:
throw new PngFeatureException("Greyscale supports 1, 2, 4, 8 but not 16.");
default:
throw new PngIntegrityException(String.format("Invalid greyscale bit-depth: %d", header.bitDepth)); // TODO: should be in header parse.
}
case PNG_GREYSCALE_WITH_ALPHA:
switch (header.bitDepth) {
case 4:
return new Greyscale4Alpha(bytesPerScanline, bitmap);
case 8:
return new Greyscale8Alpha(bytesPerScanline, bitmap);
case 16:
return new Greyscale16Alpha(bytesPerScanline, bitmap);
default:
throw new PngIntegrityException(String.format("Invalid greyscale-with-alpha bit-depth: %d", header.bitDepth)); // TODO: should be in header parse.
}
case PNG_INDEXED_COLOUR:
switch (header.bitDepth) {
case 1:
return new IndexedColourBits(bytesPerScanline, bitmap, 7, 0x01, PngConstants.SHIFTS_1);View on GitHub (pinned to 24702dc5a0)