unjs / ipx

🖼️ High performance, secure and easy-to-use image optimizer.
MIT License
1.54k stars 61 forks source link

Auto-detect animated sources #53

Closed ascorbic closed 2 years ago

ascorbic commented 2 years ago

Currently, if you want animated output you need to specifically choose it. It would be good if animated webp input images could automatically generate animated output. I'm not sure what the overhead would be of just passing animated to the sharp constructor every time.

pi0 commented 2 years ago

Hi @ascorbic. I think the limitation is that we cannot resize webp animated output. (context https://github.com/unjs/ipx/issues/35 and https://github.com/lovell/sharp/issues/2275). So usage should be with caution.

ascorbic commented 2 years ago

Ooh, that makes sense. Hmm. I guess I'll need to work out a way to pass the animated modifier through in the next/image generated URL

ascorbic commented 2 years ago

...though thinking about it, if we can't resize them, then there's not much point using next/image

pi0 commented 2 years ago

Indeed. What do you think about supporting a no-op modifier that returns source image? (can be auto enabled for .gif for example) Basically using ipx as a raw proxy in this situations so you don't need to duplicate logic.

ascorbic commented 2 years ago

Yes, we do that for svg and gif but it could be a useful addition. I'm also going to think of a syntax that can be used in next/image source URLs that will allow modifiers to be passed-through

ascorbic commented 2 years ago

If animated is passed as a modifier, would it make sense to disable resizing as it breaks animation?

pi0 commented 2 years ago

That would be a nice feature to avoid the wrong usage of the experimental animated flag! We need to disable s, w and h

pi0 commented 2 years ago

Tests:

lovell commented 2 years ago

Hello, if you hadn't seen we're currently working on both improving animated image resizing and adding GIF write support to sharp.

ascorbic commented 2 years ago

@lovell That's great! Will GIF write support be in the precompiled binaries?

lovell commented 2 years ago

@ascorbic Yes, via https://github.com/dloebl/cgif

ascorbic commented 2 years ago

That's good news! What sort of timeline is there for these two?

lovell commented 2 years ago

If you hadn't already seen, support for resizing and cropping animated GIF and WebP images was added in sharp v0.30.0.

https://sharp.pixelplumbing.com/changelog

const data = await sharp('in.gif', { animated: true })
  .resize({ width: 128, height: 128 })
  .toBuffer()
pi0 commented 2 years ago

Let's enable animated by default then! @lovell I've enabled it only for gif extensions for now. Is there any reason to not always pass animated: true when optimizing images?

tgcallaway commented 2 years ago

When returning image metadata after Sharp has processed an animated gif, I'm getting the entire 'toilet paper roll' height instead of the individual height of the pages.

Sharp(SomeGIf, { animated: true })
    .webp({ quality: 80 })
    .toBuffer((err, buf, info) => {
        if (err) {
            return reject(err);
        }
        return resolve({
            buffer: buf,
            width: info.width,
            height: info.height // This is height * pages
        });
    });

@lovell Is there any way to get the number of pages in a gif, or the individual page height, after it is converted to a Buffer from within the callback?

tgcallaway commented 2 years ago

Solved my issue with the following.

var pageHeight = false;
                const image = Sharp(reducedImage, { animated: animated });

                image
                    .metadata()
                    .then(function(metadata){
                        pageHeight = metadata.pageHeight ? metadata.pageHeight : metadata.height;
                        return image.webp({ quality: 80 })
                            .toBuffer((err, buf, info) => {
                                if (err) {
                                    return reject(err);
                                }
                                return resolve({
                                    sharp: buf,
                                    width: info.width,
                                    height: pageHeight
                                });
                            });
                    });
pi0 commented 2 years ago

Thanks for the notice @tgcallaway. Are you also facing the same problem with IPX? (FYI we use image-meta but mainly to find mime type.)

lovell commented 2 years ago

Is there any reason to not always pass animated: true when optimizing images?

@pi0 Always passing animated: true looks like the right approach for the scenarios ipx supports.

Is there any way to get the number of pages in a gif, or the individual page height, after it is converted to a Buffer from within the callback?

@tgcallaway Not directly at the moment. Adding pageHeight to the info object in the response would make a good improvement - please feel free to open an enhancement issue for this.