Skip to content

Commit

Permalink
Use global rgb565 palette
Browse files Browse the repository at this point in the history
  • Loading branch information
zk-phi committed Jun 24, 2024
1 parent eb26420 commit 2432093
Showing 1 changed file with 51 additions and 11 deletions.
62 changes: 51 additions & 11 deletions src/gifworker.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { GIFEncoder, quantize, applyPalette } from "gifenc";
import { GIFEncoder, quantize, applyPalette, nearestColorIndex } from "gifenc";

Check warning on line 1 in src/gifworker.ts

View workflow job for this annotation

GitHub Actions / lint-js

'applyPalette' is defined but never used
import { binarizeTransparency } from "./utils/binarize";

type Options = {
format: "rgb565" | "rgba4444",
delay: number,
width: number,
height: number,
Expand All @@ -16,23 +15,64 @@ const encoder = GIFEncoder();
const frames: Uint8ClampedArray[] = [];
let options: Options;

// Make an rgba565 global palette.
const quantizeGlobal = (
frames: Uint8ClampedArray[],

Check failure on line 20 in src/gifworker.ts

View workflow job for this annotation

GitHub Actions / lint-js

'frames' is already declared in the upper scope on line 15 column 7
width: number,
height: number,
) => {
const mergedFrames = new Uint8Array(frames.length * width * height * 4);
frames.forEach((frame, ix) => {
mergedFrames.set(frame, ix * width * height);
});
const palette = quantize(mergedFrames, 256, { format: "rgb565" });
return palette;
};

const genColorFinder = (palette: number[][]) => {
const cache: Record<number, number> = {};
return (rgb: [number, number, number]) => {
const key = (rgb[0] << 16) | (rgb[1] << 8) | rgb[2];
if (cache[key] === undefined) {
cache[key] = nearestColorIndex(palette, rgb);
}
return cache[key];
};
};

ctx.addEventListener("message", (msg) => {
if (msg.data.initialize) {
options = {
...msg.data.initialize,
format: msg.data.initialize.transparent ? "rgba4444" : "rgb565",
};
options = msg.data.initialize;
} else if (msg.data.addFrame) {
frames.push(msg.data.addFrame);
} else if (msg.data.finish) {
const { format, delay, transparent, width, height } = options;
frames.forEach((frame) => {
const { delay, transparent, width, height } = options;
const palette = quantizeGlobal(frames, width, height);
if (transparent && palette.length === 256) { // preserve 1 room for the transparent color
palette.pop();
}
const colorFinder = genColorFinder(palette);
const transparentIndex = palette.length;
frames.forEach((frame, frameIx) => {
// dither and binarize transparency first
if (transparent) {
binarizeTransparency(frame, width, height);
}
const palette = quantize(frame, 256, { format, oneBitAlpha: transparent });
const index = applyPalette(frame, palette, format);
encoder.writeFrame(index, width, height, { palette, delay, transparent });
// apply palette
const indexed = new Uint8Array(width * height);
for (let i = 0; i < indexed.length; i++) {

Check failure on line 63 in src/gifworker.ts

View workflow job for this annotation

GitHub Actions / lint-js

Unary operator '++' used
if (transparent && frame[i * 4 + 3] < 128) {
indexed[i] = transparentIndex;
} else {
indexed[i] = colorFinder([frame[i * 4], frame[i * 4 + 1], frame[i * 4 + 2]]);
}
}
encoder.writeFrame(indexed, width, height, {
palette: frameIx === 0 ? palette : undefined,
delay,
transparent,
transparentIndex,
});
});
encoder.finish();
ctx.postMessage(new Blob([encoder.bytes()], { type: "image/gif" }));
Expand Down

0 comments on commit 2432093

Please sign in to comment.