Fix image masks (PDFBOX-6077) - #491
Open
valerybokov wants to merge 2 commits into
Open
Conversation
A stencil image filled with a pattern draws the paint and the mask into separate scratch images and then combines them. The combine step unconditionally overwrote the paint's alpha with the mask's alpha, so any pixel the pattern itself never painted into (e.g. the gaps between tiles of a tiling pattern) turned opaque black instead of staying transparent. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
A soft mask's Paint/PaintContext looks up its backing raster using absolute page-device pixel coordinates, fixed when the soft mask group was rendered. The stencil-mask-with-pattern code renders into an isolated scratch image rather than directly onto the page graphics, so those coordinates no longer lined up and the soft mask silently applied zero alpha everywhere, making the pattern disappear. Unwrap the soft mask, fill the scratch image with its plain underlying paint instead, and apply the soft mask's own alpha afterwards by directly looking up its backing raster through a per-pixel device transform, rather than relying on the Paint/PaintContext machinery that assumed it was rendering onto the real page raster. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Contributor
|
This change produces a white line in the rendering of the file from PDFBOX-5403, in the text "Enzian Immobilien". update: I tried "fix 1" alone and had just that. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PDFBOX-6077: Fix stencil masks filled with a pattern
Summary
A PDF image used as a stencil mask (an ImageMask) can be filled with a pattern instead of a solid color. PDFBox renders this case specially: it draws the pattern's paint and the mask into two separate scratch images, then combines them pixel-by-pixel before compositing the result onto the page. Two independent bugs in that combine step caused patterns used this way to render incorrectly.
Bug 1 — mask alpha overwrote the paint's own alpha
The combine step did:
rasterPixel[3] = alphaPixel[0];
unconditionally replacing the paint's alpha with the mask's alpha. Any pixel the pattern itself never painted into — for example the gaps between tiles of a tiling pattern — has alpha 0 in the paint image, but that got overwritten with the mask's (opaque) alpha, turning transparent gaps into solid black.
Fix: combine the two alphas by multiplication instead of overwriting:
rasterPixel[3] = rasterPixel[3] * alphaPixel[0] / 255;
so a pixel is only visible where both the pattern painted something and the mask allows it through.
Bug 2 — soft-masked patterns rendered fully transparent
A pattern can itself have a soft mask applied to it (SoftMask, wrapping the pattern's own Paint). SoftMask looks up its backing (grayscale) raster using absolute page-device pixel coordinates, fixed at the point the soft mask's transparency group was rendered. Because the stencil-mask-with-pattern code fills into an isolated scratch image — not the real page Graphics2D — those coordinates no longer lined up with anything, and the soft mask silently applied zero alpha everywhere, making the pattern disappear entirely.
Fix: unwrap the soft mask, fill the scratch image with its plain underlying paint, then apply the soft mask's own alpha afterward via a new PageDrawer.applySoftMaskAlpha(), which looks up the mask's backing raster directly using a per-pixel device-coordinate transform — correct regardless of the scratch image's resolution — rather than relying on the Paint/PaintContext machinery that assumes it's rendering onto the real page raster. SoftMask gains a few narrow package-private accessors for this.
Commits
Per https://www.apache.org/legal/generative-tooling.html: portions of this PR were
produced with assistance from Claude Code (Anthropic), based on a bug was described in Apache PDFBOX Issue Tracker.
I've reviewed the generated code and confirm to the best of my knowledge that the output does not include any
third-party copyrighted material and is compatible with the Apache License 2.0.