Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How do I prevent pixelation of the GIF from 2nd frame onwards when serving it from Node Server? #39

Open
Tribevote opened this issue Jul 20, 2021 · 4 comments

Comments

@Tribevote
Copy link

The first frame seems to load fine, from 2nd frame onwards, there are black pixels. Any idea on how to get rid of them?

Pixelated Screen reference

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);


})
@xenodirt
Copy link

I'm also having the same problem. Has anyone found a fix yet?

@matt-way
Copy link
Owner

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 added a commit to flyskywhy/gifuct-js that referenced this issue Jan 5, 2023
flyskywhy added a commit to flyskywhy/react-native-pixel-gif that referenced this issue Jan 19, 2023
@flyskywhy
Copy link

@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

@eric-yates
Copy link

eric-yates commented Apr 26, 2023

@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:

  1. Keep track of last frame's disposalType.
  2. Before rendering a frame, clearRect if frameIndex is 0 or last frame's disposalType is 2.
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',
        }}
    />
))}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants