Skip to content

Commit d508db8

Browse files
authored
fix: draw html5 frames on a software canvas so Firefox keeps their gradients (#280)
1 parent 5154007 commit d508db8

2 files changed

Lines changed: 12 additions & 2 deletions

File tree

‎src/components/canvas/players/html5-player.ts‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ const BAKE_SCALES = [0.25, 0.5, 0.75, 1];
4141
// A remembered frame holds its body markup, so a window bounds the memory. 300 frames covers a 10 s loop at 30 fps.
4242
const REPEAT_WINDOW_FRAMES = 300;
4343
// Part of every bake's cache key, so raising it redraws every cached bake.
44-
const FRAME_DRAWING_VERSION = 1;
44+
const FRAME_DRAWING_VERSION = 2;
4545
const ANIMATION_OVERRIDE = `<style>*,*::before,*::after{animation:none!important;transition:none!important}</style>`;
4646

4747
// A base64 bitmap in a frame's markup: a CSS url(), an <img> source, or a canvas painted as a background.
@@ -109,7 +109,8 @@ async function foreignObjectSvgToWebp(svg: string, width: number, height: number
109109
const canvas = document.createElement("canvas");
110110
canvas.width = width * scale;
111111
canvas.height = height * scale;
112-
const ctx = canvas.getContext("2d");
112+
// Firefox's GPU canvas draws some of a frame's gradients as a solid fill. A canvas set for reading back draws in software.
113+
const ctx = canvas.getContext("2d", { willReadFrequently: true });
113114
if (!ctx) throw new Error("2D context unavailable for foreignObject rasterise");
114115
ctx.drawImage(img, 0, 0, width * scale, height * scale);
115116
const blob = await new Promise<Blob | null>(resolve => {

‎tests/html5-player.test.ts‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1050,6 +1050,15 @@ it("draws frames at the bake scale and sizes their texture back to the clip", as
10501050
expect((harness.contentContainer.children.at(-1) as PlaybackSprite).texture.resolution).toBe(0.5);
10511051
});
10521052

1053+
it("draws frames on a software canvas, since Firefox's GPU canvas fills some gradients solid", async () => {
1054+
const { harness } = createStillPlayer();
1055+
const getContext = jest.spyOn(HTMLCanvasElement.prototype, "getContext").mockReturnValue({ drawImage: jest.fn() } as never);
1056+
1057+
await harness.runCapture();
1058+
1059+
expect(getContext).toHaveBeenCalledWith("2d", { willReadFrequently: true });
1060+
});
1061+
10531062
it("draws a full-size frame of a fractional clip size as an unscaled draw does", async () => {
10541063
const { harness } = createStillPlayer();
10551064
Object.assign(harness, { renderedWidth: 1080.6, renderedHeight: 1920.4 });

0 commit comments

Comments
 (0)