Skip to content

Commit 0c66063

Browse files
authored
Merge pull request #21242 from calixteman/knockout
Render knockout transparency groups
2 parents 5bc5791 + d1e9194 commit 0c66063

12 files changed

Lines changed: 690 additions & 26 deletions

src/display/canvas.js

Lines changed: 577 additions & 26 deletions
Large diffs are not rendered by default.

src/display/filter_factory.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ class BaseFilterFactory {
4242
return "none";
4343
}
4444

45+
addKnockoutFilter(alpha = 0) {
46+
return "none";
47+
}
48+
4549
addHighlightHCMFilter(filterName, fgColor, bgColor, newFgColor, newBgColor) {
4650
return "none";
4751
}
@@ -329,6 +333,37 @@ class DOMFilterFactory extends BaseFilterFactory {
329333
return url;
330334
}
331335

336+
addKnockoutFilter(alpha = 0) {
337+
// Shape alpha mask: for translucent elements, remove the opacity constant
338+
// from the painted alpha while preserving antialias coverage. With no
339+
// usable opacity, fall back to a binary mask.
340+
const slope = alpha > 0 ? Math.min(1 / alpha, 1e6) : 1e6;
341+
const key = `knockout_${slope}`;
342+
const value = this.#cache.get(key);
343+
if (value) {
344+
return value;
345+
}
346+
347+
const id = `g_${this.#docId}_knockout_filter_${this.#id++}`;
348+
const url = this.#createUrl(id);
349+
this.#cache.set(key, url);
350+
351+
const filter = this.#createFilter(id);
352+
const feComponentTransfer = this.#document.createElementNS(
353+
SVG_NS,
354+
"feComponentTransfer"
355+
);
356+
filter.append(feComponentTransfer);
357+
const feFuncA = this.#document.createElementNS(SVG_NS, "feFuncA");
358+
// Linear feFunc clamps to [0, 1].
359+
feFuncA.setAttribute("type", "linear");
360+
feFuncA.setAttribute("slope", `${slope}`);
361+
feFuncA.setAttribute("intercept", "0");
362+
feComponentTransfer.append(feFuncA);
363+
364+
return url;
365+
}
366+
332367
addHighlightHCMFilter(filterName, fgColor, bgColor, newFgColor, newBgColor) {
333368
const key = `${fgColor}-${bgColor}-${newFgColor}-${newBgColor}`;
334369
let info = this.#hcmCache.get(filterName);

src/shared/util.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,21 @@ class FeatureTest {
657657
});
658658
}
659659

660+
static get isCanvasFilterSupported() {
661+
let ctx;
662+
if (this.isOffscreenCanvasSupported) {
663+
ctx = new OffscreenCanvas(1, 1).getContext("2d");
664+
} else if (typeof document !== "undefined") {
665+
ctx = document.createElement("canvas").getContext("2d");
666+
}
667+
// Spec-compliant Canvas2D defaults `ctx.filter` to "none". On
668+
// browsers without filter support (Safari) the property is absent
669+
// until you assign to it, after which it behaves like an ordinary
670+
// JS property and stores whatever string you set without applying
671+
// it. Probing the default lets us detect the difference reliably.
672+
return shadow(this, "isCanvasFilterSupported", ctx?.filter !== undefined);
673+
}
674+
660675
static get isAlphaColorInputSupported() {
661676
return shadow(
662677
this,

test/pdfs/.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@
1616
!bug1727053.pdf
1717
!issue18408_reduced.pdf
1818
!bug1907000_reduced.pdf
19+
!knockout_blend_multiply.pdf
20+
!knockout_isolated_overlap.pdf
21+
!knockout_nested.pdf
22+
!knockout_nested_group_alpha.pdf
23+
!knockout_nonisolated_sparse.pdf
24+
!knockout_smask.pdf
1925
!SimFang-variant.pdf
2026
!bug1953099.pdf
2127
!issue11913.pdf
@@ -913,3 +919,4 @@
913919
!smask_alpha_bc.pdf
914920
!smask_luminosity_oob_transfer.pdf
915921
!operator_list_cycle.pdf
922+
!knockout_groups_test.pdf
900 Bytes
Binary file not shown.

test/pdfs/knockout_groups_test.pdf

24.7 KB
Binary file not shown.
901 Bytes
Binary file not shown.

test/pdfs/knockout_nested.pdf

1.1 KB
Binary file not shown.
1.14 KB
Binary file not shown.
870 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)