Reputation: 401
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?
EDIT : Using globalPalette
and a large quality
(20), I still get this weird pulsating of the logo but the colors are consistent.
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
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