mattdesl / gifenc

fast GIF encoding
MIT License
277 stars 19 forks source link

Dispose 2 not working with transparent gifs #15

Open nicolaschapeau opened 11 months ago

nicolaschapeau commented 11 months ago

Hey!

Great work with the library it's really fast!

I stumbled on a problem recently which is transparent gifs seems to have each frame stack on each other.

I looked at the source code and found out I had to put dispose = 2 to fix that but it seems to not work.

Here is my code, it's a simple worker that take a gif as a data64 url and newDuration. The goal is simple modify the duration of the gif by adding or removing delay between frames.

async function adjustGifDuration({ src, newDuration }: { src: string; newDuration: number }) {
    // Get frames from gif
    const frames = await gifFrames({
        url: src,
        frames: 'all',
        outputType: 'png',
        cumulative: true
    });

    // Get current duration
    const currentDuration =
        frames.reduce((total: any, frame: any) => total + frame.frameInfo.delay, 0) / 100;

    // Calculate ratio
    const ratio = newDuration / currentDuration;

    // Create encoder
    const encoder = new GIFEncoder();

    // Create palette rgba 256
    const rgbaData = frames[0].getImage().data;
    const palette = quantize(rgbaData, 256, {
        format: 'rgba4444'
    });

    for (let i = 0; i < frames.length; i++) {
        const frame = frames[i];

        // Get frame image
        const image = frame.getImage().data;

        // Apply palette to image
        const index = applyPalette(image, palette, 'rgba4444'); //rgba4444

        // Add frame to encoder
        encoder.writeFrame(index, frame.getImage().width, frame.getImage().height, {
            palette,
            delay: frame.frameInfo.delay * ratio,
            transparent: true,
            transparentIndex: 0,
            dispose: 2
        });

        // Update progress
        postMessage({ type: 'progress', progress: (i / frames.length) * 100 });
    }

    // Write end-of-stream character
    encoder.finish();

    // Get the Uint8Array output of the binary GIF file
    const output = encoder.bytes();

    // Convert the Uint8Array to a Buffer object
    const buffer = Buffer.from(output);

    return buffer;
}

Everything else seems to work fine, but this is a kind of a huge problem for me, would be glad to know if I did something wrong or if there is a problem with the library!

Regards, Nicolas.

https://github.com/mattdesl/gifenc/assets/32775238/35551d74-af10-463d-a5a2-de3aab5488b5

shankiflang commented 11 months ago

I had the same problem and couldn't find a solution! Maybe here we'll have an answer! @mattdesl ?

mattdesl commented 11 months ago

I haven't attempted to run your code but in my own tests with the Looom exporter, transparency seems to have worked OK.

Here's how I've set it up: https://github.com/mattdesl/looom-tools/blob/main/site/components/gifworker.js

If there is a reproducible test case showing the error I can dig into it a bit more.