1

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();
    })
},

Curious
  • 383
  • 3
  • 13
  • 1
    Are you loading 6 separate gif images or a single animated gif with 6 embedded frames? In the animated gif used in the question, frames 1 and 6 have a faded logo in the bottom right causing the flash. Yet, without more information it would be difficult to pinpoint the cause. – Yogi Apr 11 '22 at 00:18
  • Thank Mr. @Yogi . I have added more code (hopefully not cluttering) but essentially I have a for loop which adds `HTMLCanvasElement`s to the `GIF` object. – Curious Apr 11 '22 at 00:27

1 Answers1

1

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.

Fabian Iwand
  • 240
  • 1
  • 9
  • Thank you Mr. Iwand. Your help does appear to improve the color problem, but the pulsating of the logo remains. Do you have knowledge of another hidden parameter that would get rid of that? – Curious Apr 11 '22 at 20:11
  • @YordanRadev not without seeing the updated code. The example app to which you shared the link in your initial post has no reference to globalPalette. – Fabian Iwand Apr 11 '22 at 20:38
  • I just updated it sir. But I simply added the `globalPalette: true` line to the constructor which fixed the color issue but not the flickering issue. – Curious Apr 11 '22 at 22:03
  • Quality shouldn't have any effect after the first frame, as colors for subsequent frames are only quantized when globalPalette = false. Note that the edges of your logo will be antialiased, resulting in additional colors. If these colors aren't in your globalPalette, they may be affected by dithering. – Fabian Iwand Apr 12 '22 at 13:34
  • I think you can also leave parts of a frame transparent, so that the data of the previous frame is kept. Perhaps it's worth a try to only draw the footer in the first frame. – Fabian Iwand Apr 12 '22 at 13:36
  • Thank you sir, but I am creating new canvas elements at each step of the for loop. This issue has also made me realize perhaps ffmpeg is a better option if I can get it working as in this [question](https://stackoverflow.com/questions/71833373/ffmpeg-js-to-create-mp4-from-canavas-frames-and-transcode-it?noredirect=1#comment126940599_71833373) – Curious Apr 12 '22 at 13:39
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/243827/discussion-between-fabian-iwand-and-yordan-radev). – Fabian Iwand Apr 12 '22 at 14:18