HMCL-dev/HMCL · error · PngIntegrityException
Unsupported blendOp at frame
Error message
Unsupported blendOp at frame
What it means
Thrown by ImageUtils.toImage as PngIntegrityException when an APNG frame's blend_op is neither APNG_BLEND_OP_SOURCE (0) nor APNG_BLEND_OP_OVER (1). Only those two blend operations are defined by the APNG spec; anything else means a corrupt/invalid chunk.
Solutions
- Re-encode the animation with a standard tool so blend_op is 0 or 1
- Replace the file (corruption is the usual cause — no encoder legitimately emits other values)
- Catch PngIntegrityException and fall back to a static image or placeholder
- Sanity-check chunk fields during parse and reject non-spec APNGs early
Example fix
// before
Image anim = ImageUtils.createAnimationFromAPNGApngStream(in);
// after
try {
anim = ImageUtils.createAnimationFromAPNGApngStream(in);
} catch (PngIntegrityException e) {
LOG.warning("Unsupported APNG feature: " + e.getMessage());
anim = fallbackStaticImage;
} Defensive patterns
Strategy: try-catch
Validate before calling
if (control.blendOp != 0 && control.blendOp != 1) {
throw new IllegalArgumentException("Non-spec blendOp: " + control.blendOp);
} Type guard
static boolean hasKnownBlendOp(PngFrameControl c) {
return c.blendOp == 0 || c.blendOp == 1; // SOURCE or OVER
} Try / catch
try {
return ImageUtils.toImage(sequence, w, h, ...);
} catch (PngIntegrityException e) {
return defaultImage; // static fallback
} Prevention
- Only accept APNGs from encoders known to emit spec blend ops
- Treat unexpected enum values in image chunks as corruption
- Keep a static-image fallback path for all animated resources
- Re-encode questionable files with ffmpeg before use
When it happens
Trigger: Decoding an APNG whose fcTL chunk carries a blend_op value other than 0 or 1 (e.g. 2+ from a corrupted or maliciously crafted file).
Common situations: Bit-rot or corruption in stored APNG files; crafted test images; outputs of experimental encoders.
Related errors
- Unsupported disposeOp at frame
- Invalid first frame:
- Invalid frame control:
- acTL chunk length must be
- fctl chunk expected sequence
AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10).
Data as JSON: /api/errors/be5873b6e083a350.
Report an issue: GitHub.
Appendix: source
Thrown at HMCL/src/main/java/org/jackhuang/hmcl/ui/image/ImageUtils.java:663
if (outAlpha == 0) {
outR = outG = outB = 0;
} else {
outR = (srcR * srcAlpha + dstR * dstAlpha * invSrcAlpha / 255 + outAlpha / 2) / outAlpha;
outG = (srcG * srcAlpha + dstG * dstAlpha * invSrcAlpha / 255 + outAlpha / 2) / outAlpha;
outB = (srcB * srcAlpha + dstB * dstAlpha * invSrcAlpha / 255 + outAlpha / 2) / outAlpha;
}
outAlpha = Math.min(outAlpha, 255);
outR = Math.min(outR, 255);
outG = Math.min(outG, 255);
outB = Math.min(outB, 255);
currentFrameBuffer[dstIndex] = (outAlpha << 24) | (outR << 16) | (outG << 8) | outB;
}
}
}
} else {
throw new PngIntegrityException("Unsupported blendOp " + control.blendOp + " at frame " + frameIndex);
}
if (doScale)
framePixels[frameIndex] = scale(currentFrameBuffer,
width, height,
targetWidth, targetHeight);
else
framePixels[frameIndex] = currentFrameBuffer;
if (control.delayNumerator == 0) {
durations[frameIndex] = 10;
} else {
int durationsMills = 1000 * control.delayNumerator;
if (control.delayDenominator == 0)
durationsMills /= 100;
else
durationsMills /= control.delayDenominator;
View on GitHub (pinned to 24702dc5a0)