mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-11-01 22:19:10 -06:00
fix(render): pattern-filled stencil masks take the fill paint at the opacity once (#2975)
Review follow-up. A pattern paint keeps the fill or stroke opacity it was set with (`scn`), so resetting the graphics state's fill alpha did not stop a pattern-filled stencil mask from being drawn at the opacity squared: `Pattern::without_opacity` now drops it for the mask's paint (patch 35). The renderer also painted a pattern-filled stencil mask with the stroke paint, so an uncoloured tiling pattern drew in its stroke colour (black by default); it now uses the fill paint. The stencil test covers a colour fill, an uncoloured tiling pattern and a shading pattern at ca 1, 0.5 and 0.25; each of the three changes, removed, makes it fail. Rebased onto main; the vendored hayro-interpret patch is now number 35 (36 was taken on main). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RUNQSPVXx3dDBabRZHAnjz
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
1cf6ca5579
commit
785d10724f
+32
-20
@@ -5825,38 +5825,50 @@ trailer << /Root 1 0 R >>
|
||||
/// #2975: a stencil mask (`/ImageMask true`) is painted in the fill colour at the fill
|
||||
/// opacity, once: at `ca 0.5` it is as opaque as a rectangle filled at `ca 0.5` beside it
|
||||
/// (it was a quarter as opaque, the opacity applied by both the image's group and its paint).
|
||||
/// The same holds when the fill is a pattern, whose paint keeps the opacity in force when it
|
||||
/// was set with `scn`: an uncoloured tiling pattern and a shading pattern.
|
||||
#[test]
|
||||
fn stencil_masks_are_drawn_at_the_fill_opacity_once() {
|
||||
let px = |p: &RenderedPage, x: u32, y: u32| p.rgba[((y * p.width + x) * 4) as usize..][..3].to_vec();
|
||||
for alpha in ["1", "0.5", "0.25"] {
|
||||
let content = "/A gs 0 0 1 rg q 100 0 0 100 20 50 cm /Msk Do Q 160 50 100 100 re f";
|
||||
let pdf = format!(
|
||||
"%PDF-1.7
|
||||
let fills = [("colour", "0 0 1 rg"), ("uncoloured tiling pattern", "/CsP cs 0 0 1 /Tp scn"), ("shading pattern", "/Pattern cs /Sp scn")];
|
||||
for (fill_name, fill) in fills {
|
||||
for alpha in ["1", "0.5", "0.25"] {
|
||||
let content = format!("/A gs {fill} q 100 0 0 100 20 50 cm /Msk Do Q 160 50 100 100 re f");
|
||||
let tile = "0 0 10 10 re f";
|
||||
let pdf = format!(
|
||||
"%PDF-1.7
|
||||
1 0 obj << /Type /Catalog /Pages 2 0 R >> endobj
|
||||
2 0 obj << /Type /Pages /Kids [3 0 R] /Count 1 >> endobj
|
||||
3 0 obj << /Type /Page /Parent 2 0 R /MediaBox [0 0 300 200] /Contents 4 0 R /Resources << /ExtGState << /A << /ca {alpha} >> >> /XObject << /Msk 5 0 R >> >> >> endobj
|
||||
3 0 obj << /Type /Page /Parent 2 0 R /MediaBox [0 0 300 200] /Contents 4 0 R /Resources << /ExtGState << /A << /ca {alpha} >> >> /XObject << /Msk 5 0 R >> /ColorSpace << /CsP [/Pattern /DeviceRGB] >> /Pattern << /Tp 6 0 R /Sp 7 0 R >> >> >> endobj
|
||||
4 0 obj << /Length {} >> stream
|
||||
{content}
|
||||
endstream endobj
|
||||
5 0 obj << /Type /XObject /Subtype /Image /Width 2 /Height 2 /ImageMask true /Length 2 >> stream
|
||||
| ||||