Curious
Curious

Reputation: 401

GIF.js flashing on addFrame

I am using GIF.js and am trying to get rid of this flashing of the logo in the bottom left corner of the image (example attached below or can be reproduced at this link. I have confirmed that each frame passed to the gif object is indeed the right color. I am using the following constructor :

var tempGIF = new GIF({
                workers: 4,
                quality: quality,
                height: this.mapHeight + this.infoCanvas.height,
                width: this.mapWidth,
                workerScript: 'gif.worker.js',
                globalPalette: true
            });

and adding frames using gif.addFrame(composedCnv, { delay: delayInput }); and although I experimented with background and transparent I am unable to get rid of the flashing behaviour. Can anyone help me please?

enter image description here

EDIT : Using globalPalette and a large quality (20), I still get this weird pulsating of the logo but the colors are consistent.

enter image description here

EDIT 2 : I dug in the source code of GIF.js and found in this link the following piece of information :

/*
  Sets quality of color quantization (conversion of images to the maximum 256
  colors allowed by the GIF specification). Lower values (minimum = 1)
  produce better colors, but slow processing significantly. 10 is the
  default, and produces good color mapping at reasonable speeds. Values
  greater than 20 do not yield significant improvements in speed.
*/

and indeed my flashing issue disappears if the quality is equal to 1. I will leave this question open in case there is a workaround and if someone can explain to me how come this quality issue only affects the logo and not the map portion of the canvas.

EDIT : Add more code for context. I loop over visible layers of a OpenLayers map and compose a HTMLCanvasElement which I addFrame to a GIF object. I have verified that the passed canvas elements are always correctly colored and have isolated the issue to be something that happens between me adding them to the GIF and the GIF being rendered.

async createGIFHandler ( layer , addAllTitles , quality , delay , range ) {
    ...
    var tempGIF = new GIF({
        workers: 4,
        quality: quality,
        height: this.mapHeight + this.infoCanvas.height,
        width: this.mapWidth,
        workerScript: 'gif.worker.js'
    });
    let progressCounter = 1;
    for ( let i = range[0] ; i < range[1] ; i++, progressCounter++ ) {
        this.setDateTime( driver , driverDA[i] );
        for ( let j = 0 ; j < visibleLayers.length ; j++ ) {
            if ( visibleLayers[j].get('layerName') !== layer.Name  ) {
                var tempDA = visibleLayers[j].get('layerDateArray');
                for ( let k = 0 ; k < tempDA.length ; k++ ) {
                    if ( driverDA[i].getTime() === tempDA[k].getTime() ) {
                        this.setDateTime( visibleLayers[j] , tempDA[k] );
                    }
                }
            }
        }
        await new Promise(resolve => this.map.once('rendercomplete', resolve));
        await this.composeCanvas( tempGIF , driverDA[i] , visibleLayers , delay , widths )
        this.$store.dispatch('Layers/setGIFPercent', Math.round(((progressCounter / gifLength) * 100)))
    }

    tempGIF.on('finished', (blob) => {
        const tempURL = URL.createObjectURL( blob )
        this.$store.dispatch( 'Layers/setGIFURL' , tempURL )
        console.log('GIF Finished');
    });
    tempGIF.render();
},
async composeCanvas( gif , timeStep , visibleLayers , delayInput , widths ) {
    const mapCnv = this.getMapCanvas();
    await this.updateInfoCanvas( timeStep , widths )
    const composedCnv = await this.stitchCanvases( mapCnv , visibleLayers.length );
    await new Promise((resolve) => {
        gif.addFrame(composedCnv, { delay: delayInput });
        resolve();
    })
},

Upvotes: 1

Views: 448

Answers (1)

Fabian Iwand
Fabian Iwand

Reputation: 273

gif.js computes the color palette per frame, but there's an undocumented option that lets you define a global palette.

To reuse the palette of the first frame, instantiate with:

new GIF({
  // ...
  globalPalette: true,
})

You can also pass in your own palette as a flat array of RGB values. E.g., to only assign black, grey and white, instantiate with:

new GIF({
  // ...
  globalPalette: [0, 0, 0, 127, 127, 127, 255, 255, 255],
})

I recommend you pick the latter option and include the logo color.

Upvotes: 1

Related Questions