Skip to content

Commit 67ec760

Browse files
committed
fix: draw html5 frames on a software canvas so Firefox keeps their gradients
1 parent baaecce commit 67ec760

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.
@@ -104,7 +104,8 @@ async function foreignObjectSvgToWebp(svg: string, width: number, height: number
104104
const canvas = document.createElement("canvas");
105105
canvas.width = width * scale;
106106
canvas.height = height * scale;
107-
const ctx = canvas.getContext("2d");
107+
// Firefox's GPU canvas draws some of a frame's gradients as a solid fill. A canvas set for reading back draws in software.
108+
const ctx = canvas.getContext("2d", { willReadFrequently: true });
108109
if (!ctx) throw new Error("2D context unavailable for foreignObject rasterise");
109110
ctx.drawImage(img, 0, 0, width * scale, height * scale);
110111
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
@@ -1037,6 +1037,15 @@ it("draws frames at the bake scale and sizes their texture back to the clip", as
10371037
expect((harness.contentContainer.children.at(-1) as PlaybackSprite).texture.resolution).toBe(0.5);
10381038
});
10391039

1040+
it("draws frames on a software canvas, since Firefox's GPU canvas fills some gradients solid", async () => {
1041+
const { harness } = createStillPlayer();
1042+
const getContext = jest.spyOn(HTMLCanvasElement.prototype, "getContext").mockReturnValue({ drawImage: jest.fn() } as never);
1043+
1044+
await harness.runCapture();
1045+
1046+
expect(getContext).toHaveBeenCalledWith("2d", { willReadFrequently: true });
1047+
});
1048+
10401049
it("draws a full-size frame of a fractional clip size as an unscaled draw does", async () => {
10411050
const { harness } = createStillPlayer();
10421051
Object.assign(harness, { renderedWidth: 1080.6, renderedHeight: 1920.4 });

0 commit comments

Comments
 (0)