weserv / images

Source code of wsrv.nl (formerly images.weserv.nl), to be used on your own server(s).
https://wsrv.nl/
BSD 3-Clause "New" or "Revised" License
1.97k stars 193 forks source link

Webp / GIF animation flickering #298

Closed fabian-emilius closed 2 years ago

fabian-emilius commented 3 years ago

When resizing an animated image, it often starts flickering even though it does not change position in the original image. Example:

Source: https://cloudflare-ipfs.com/ipfs/Qmbiub1pBEjxTKWfw82FZ87L9p9gDsEi8K2MWdMJxQz2ZK/Kog/foil/common/1-7-3-24-1-0_front.webp Resized: https://images.weserv.nl/?h=370&w=370&fit=inside&we&q=75&output=webp&n=-1&url=https://cloudflare-ipfs.com/ipfs/Qmbiub1pBEjxTKWfw82FZ87L9p9gDsEi8K2MWdMJxQz2ZK/Kog/foil/common/1-7-3-24-1-0_front.webp

kleisauke commented 3 years ago

I was able to reproduce this using the vips CLI utility.

$ vips copy 1-7-3-24-1-0_front.webp[n=-1,scale=0.308333] x.webp

It appears, for this particular image, that almost all sub-frames are positioned as (left = 200, top = 200, width = 800, height = 800), but some sub-frames are positioned differently from the rest.

Details ```bash $ webpinfo 1-7-3-24-1-0_front.webp | grep -B 2 -A 5 "Offset_Y: 198" Chunk ANMF at offset 702858, length 116260 Offset_X: 200 Offset_Y: 198 Width: 800 Height: 802 Duration: 80 Dispose: 0 Blend: 1 -- Chunk ANMF at offset 819118, length 118152 Offset_X: 200 Offset_Y: 198 Width: 800 Height: 802 Duration: 80 Dispose: 0 Blend: 1 -- Chunk ANMF at offset 937270, length 114618 Offset_X: 200 Offset_Y: 198 Width: 800 Height: 803 Duration: 80 Dispose: 0 Blend: 1 -- Chunk ANMF at offset 2041856, length 81806 Offset_X: 200 Offset_Y: 198 Width: 800 Height: 802 Duration: 80 Dispose: 0 Blend: 1 $ vips copy 1-7-3-24-1-0_front.webp[n=-1,scale=0.308333] x.webp | grep -B 2 -A 6 "top = 61" webp2vips: frame_num = 8 left = 62 top = 61 width = 247 height = 247 duration = 80 dispose = none has_alpha = 1 blend_method = don't blend -- webp2vips: frame_num = 9 left = 62 top = 61 width = 247 height = 247 duration = 80 dispose = none has_alpha = 1 blend_method = don't blend -- webp2vips: frame_num = 10 left = 62 top = 61 width = 247 height = 248 duration = 80 dispose = none has_alpha = 1 blend_method = don't blend -- webp2vips: frame_num = 22 left = 62 top = 61 width = 247 height = 247 duration = 80 dispose = none has_alpha = 1 blend_method = don't blend ```

This issue only seems to occur when libvips opens the image with the "scale-on-load" feature, which also resizes these sub-frames to its target size. https://github.com/libvips/libvips/blob/3732751bf9d22b00ae33d6701e9a823c4d1bea87/libvips/foreign/webp2vips.c#L636-L643

You mentioned that GIF images also have this issue, do you have an example of that?

@jcupitt Do you think this can be fixed somehow in libvips?

fabian-emilius commented 3 years ago

Original: https://cloudflare-ipfs.com/ipfs/QmXe65mTMhPiBPJ1tzeYXF6btHQy2QwwxNTkjgnAHsRLmu Resized: https://images.weserv.nl/?h=370&w=370&fit=inside&we&q=75&output=webp&n=-1&url=https://cloudflare-ipfs.com/ipfs/QmXe65mTMhPiBPJ1tzeYXF6btHQy2QwwxNTkjgnAHsRLmu

fabian-emilius commented 3 years ago

Ah sorry thats also webp, so probably only happens for webp images

jcupitt commented 3 years ago

@kleisauke I tried:

vips copy 1-7-3-24-1-0_front.webp[n=-1,scale=0.308333] x.gif

To make:

x

I see a slight up/down jiggle on the bottom edge, is that the error?

kleisauke commented 3 years ago

Yes exactly, for comparison, I disabled the "scale-on-load" feature for WebP images and ran:

$ vipsthumbnail 1-7-3-24-1-0_front.webp[n=-1] -s 370x -o x.gif

To make: x

jcupitt commented 3 years ago

Ah I see! Yes, I bet we need to round the webp shrink factor to be 1 / multiple-of-page-height. I'll have a go.

kleisauke commented 2 years ago

Upstream issue https://github.com/libvips/libvips/issues/2379 is now fixed and I just updated the production server to incorporate this fix (commit https://github.com/weserv/rpms/commit/fb874e6e0e17d7e2b5a1c1882a93632b5787e7af). This animated WebP image is now resized without juddering.

If you want to test this, you can use the RPMS repo from https://rpms.weserv.nl. This change in the Dockerfile is required for this:

--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -33,8 +33,10 @@ RUN rpm --import https://sourceforge.net/projects/libjpeg-turbo/files/LJT-GPG-KE
     && dnf install -y https://rpms.remirepo.net/enterprise/remi-release-8.rpm \
     && rpmkeys --import file:///etc/pki/rpm-gpg/RPM-GPG-KEY-remi.el8 \
     && dnf config-manager --set-enabled remi \
-    && dnf install -y --nogpgcheck https://download1.rpmfusion.org/free/el/rpmfusion-free-release-8.noarch.rpm \
-    && rpmkeys --import file:///etc/pki/rpm-gpg/RPM-GPG-KEY-rpmfusion-free-el-8 \
+    && dnf config-manager --set-disabled remi-safe \
+    && dnf config-manager --set-disabled remi-modular \
+    && sed -i '/^\[remi\]/a\excludepkgs=vips*' /etc/yum.repos.d/remi.repo \
+    && dnf config-manager --add-repo https://rpms.weserv.nl/weserv.repo \
     && dnf group install -y --with-optional 'Development Tools' \
     && dnf install -y --setopt=tsflags=nodocs --setopt=install_weak_deps=False \
         vips-devel \
kleisauke commented 2 years ago

libvips v8.12.0 has been landed on Remi's RPM repository with commit faba5a2, so a recompilation of the Docker image (or using the prebuilt ghcr.io/weserv/images:5.x image) should automatically incorporate this fix.