gifuct-js
gifuct-js copied to clipboard
How do I prevent pixelation of the GIF from 2nd frame onwards when serving it from Node Server?
The first frame seems to load fine, from 2nd frame onwards, there are black pixels. Any idea on how to get rid of them?
Here is my code in NodeJS
var GIFEncoder = require('gifencoder');
const { createCanvas, loadImage, giflib, ImageData } = require('canvas')
const canvas = createCanvas(600, 400)
const ctx = canvas.getContext('2d')
const { parseGIF, decompressFrames } = require('gifuct-js')
app.get('/gif2', async function(req, res) {
const gifURL = 'https://www.hubspot.com/hubfs/Smiling%20Leo%20Perfect%20GIF.gif'
const gif = await fetch(gifURL)
.then(resp => resp.arrayBuffer())
.then(buff => parseGIF(buff));
const frames = await decompressFrames(gif,true);
const encoder = new GIFEncoder(600, 400);
// stream the results as they are available into myanimated.gif
encoder.createReadStream().pipe(fs.createWriteStream('myanimated.gif'));
encoder.start()
encoder.setDelay(200)
for (let frameIndex=0; frameIndex < frames.length; frameIndex++) {
try {
const frame = frames[frameIndex]
const imageData = new ImageData(
frame.patch,
frame.dims.width,
frame.dims.height
);
ctx.putImageData(imageData, 0, 0);
// Add Text on Canvas
let label = req.query.name
ctx.font = '30px Impact'
ctx.fillStyle = "white";
wrapText(ctx, label, 50, 50, 200, 35)
encoder.addFrame(ctx);
} catch (error) {
console.log("Something went wrong>>>>", error);
}
}
encoder.finish();
console.log("Encoding Done>>>>>");
setTimeout(function() {
fs.readFile('myanimated.gif', function(err, data) {
console.log("Serve it Waiter>>>>>");
res.writeHead(200, {'content-type':'image/gif'});
res.end(data);
})
}, 300);
})
I'm also having the same problem. Has anyone found a fix yet?
Playing the GIF you supplied in the gifuct sample player seems to playback fine without artifacts. Could it be something to do with the encoder?
@flyskywhy/gifuct-js add imageData besides patch, here imageData of every frame is full size. Or you can find out how small size patch (from 2nd frame) be converted into full size imageData in the source code, and use them in your APP code with official gifuct-js.
Also you may ref to pixel-gif and it's react-native version react-native-pixel-gif
@Tribevote @xenodirt I faced this issue too and figured out the solution (at least in my case) to rendering the frames from this old issue:
- Keep track of last frame's
disposalType
. - Before rendering a frame,
clearRect
ifframeIndex
is0
or last frame'sdisposalType
is2
.
if (i === 0 || prevDisposalType === 2) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
Here's a link to my full code (I had to use a temp canvas for some reason to get it working):
fetch(gifUrl)
.then(response => response.arrayBuffer())
.then(buffer => {
let gif = parseGIF(buffer);
let decompressedFrames = decompressFrames(gif, true);
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = gif.lsd.width;
canvas.height = gif.lsd.height;
let prevDisposalType;
const frameDataURLs = decompressedFrames.map((frame, i) => {
const tempCanvas = document.createElement('canvas');
const tempCtx = tempCanvas.getContext('2d');
tempCanvas.width = frame.dims.width;
tempCanvas.height = frame.dims.height;
const frameImageData = tempCtx.createImageData(frame.dims.width, frame.dims.height);
frameImageData.data.set(frame.patch);
tempCtx.putImageData(frameImageData, 0, 0);
if (i === 0 || prevDisposalType === 2) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
ctx.drawImage(tempCanvas, frame.dims.left, frame.dims.top);
prevDisposalType = frame.disposalType;
return canvas.toDataURL('image/png');
});
});
I don't think this will be useful for OP, although here's how I rendered it if it helps anyone else:
{frameDataURLs.map((frameDataURL, index) => (
<img
key={index}
src={frameDataURL}
alt={`Frame ${index}`}
style={{
display: index === currentIdx ? 'block' : 'none',
}}
/>
))}