SixLabors / ImageSharp

:camera: A modern, cross-platform, 2D Graphics library for .NET
https://sixlabors.com/products/imagesharp/
Other
7.41k stars 852 forks source link

Use gif to converted have animation webp, but this converted webp image can not import What's app software #2825

Open Dylan-Jinx opened 1 week ago

Dylan-Jinx commented 1 week ago

Prerequisites

ImageSharp version

3.1.5

Other ImageSharp packages and versions

3.1.5

Environment (Operating system, version and so on)

MacOS

.NET Framework version

.NET8

Description

I am using a GIF image to convert it into an animated WebP, but the resulting WebP image cannot be imported into WhatsApp. It seems to be filtered or rejected by WhatsApp. However, when I use the Magick library to perform the same conversion, the WebP file is successfully imported into WhatsApp.

Here is the code I am using to convert the GIF to WebP:

var gifImage = Image.Load(image);
gifImage.SaveAsWebp(Path.Combine(AppContext.BaseDirectory, "testImageSharp.webp"));

I would like to know if there is a specific issue with my code or any known differences between the encoding methods that could be causing this problem.

Steps to Reproduce

var gifImage = Image.Load(image);
gifImage.SaveAsWebp(Path.Combine(AppContext.BaseDirectory, "testImageSharp.webp"));

Images

No response

JimBobSquarePants commented 1 week ago

Can you please supply a sample image? It’s critical to allow me to debug anything.

dlemstra commented 5 days ago

Please provide an image when you are asked for one. If we find an image that works and it still doesn't work with yours than you are wasting our time.

Dylan-Jinx commented 5 days ago

@JimBobSquarePants @dlemstra I apologize for my earlier oversight. Since the images we're using have copyright considerations, could you please share your email address? I would like to send the problematic images directly to you. Thank you!

JimBobSquarePants commented 5 days ago

help @ sixlabors.com

Dylan-Jinx commented 5 days ago

Thank you very much! I will send the organized data to the specified email address shortly.

Dylan-Jinx commented 4 days ago

@JimBobSquarePants I've sent the relevant data to your email. Please take a look when you have a moment. I really appreciate all your hard work. Thank you!

JimBobSquarePants commented 3 days ago

I got your image and had a look. Thanks for sending it through.

The good news is that I've tested it against both webpinfo and ezgif splitter and cannot find an issue with it.

https://developers.google.com/speed/webp/docs/webpinfo https://ezgif.com/split

I'm also able to play the WEBP successfully using vwebp, Edge and Firefox on my Windows development machine. https://developers.google.com/speed/webp/docs/vwebp

So, we know the format of the image is fine. 👍

Let's look deeper at the image though...

The provided input GIF file has a background color set at index 63 in the color palette. This color is equivalent to RGBA 255, 255, 255, 255 which is white.

ImageSharp will use that information when translating the GIF to a WEBP file to set the background color on the ANIM chunk in the RIFF container. Here's what the specification says about that property.

Background Color: 32 bits (uint32) The default background color of the canvas in [Blue, Green, Red, Alpha] byte order. This color MAY be used to fill the unused space on the canvas around the frames, as well as the transparent pixels of the first frame. The background color is also used when the Disposal method is 1.

Note:

  • The background color MAY contain a nonopaque alpha value, even if the Alpha flag in the 'VP8X' Chunk (Figure 7) is unset.
  • Viewer applications SHOULD treat the background color value as a hint and are not required to use it.
  • The canvas is cleared at the start of each loop. The background color MAY be used to achieve this.

https://developers.google.com/speed/webp/docs/riff_container#animation

It looks to me like the specific decoder which displays the thumbnails is using that background color property to fill outside frames which is different behavior to the decoders on my Windows machine. (ImageSharp optimizes the encoded frames on encode to remove unused areas)

Fortunately, it's possible to update that property.


using Image gifImage = Image.Load(image);

// Manually copy the properties across.
GifMetadata gMeta = gifImage .Metadata.GetGifMetadata();
WebpMetadata wMeta = gifImage .Metadata.GetWebpMetadata();
wMeta.FileFormat = WebpFileFormatType.Lossless;
wMeta.RepeatCount = gMeta.RepeatCount;
wMeta.BackgroundColor = Color.Transparent;

gifImage.SaveAsWebp(Path.Combine(AppContext.BaseDirectory, "testImageSharp.webp"));

Note: The RepeatCount and BackgroundColor properties are current missing on the WebPEncoder type. I'll add them in an update which will make this easier to set.

P.S. Your code sample showed you are not disposing of your images. You must do that to prevent memory leaks.

Dylan-Jinx commented 3 days ago

Thank you very much! After using this code to generate a webp file, I found that there are no flickering issues when previewing it on my Mac. Everything seems to be normal so far. 👍

However, when I try to import it as a sticker in WhatsApp, it gets filtered out (I suspect WhatsApp considers it invalid and excludes it?).

For this image, when I directly convert the gif to webp using the Magick.NET library, it displays correctly. I wonder if there might be some subtle issue causing this.

I can provide an example of the code I used with Magick.NET.

using (var collection = new MagickImageCollection())
{
    collection.Read(image);
    var webpSettings = new WebPWriteDefines
    {
        Lossless = true,
    };
    using (var ms = new MemoryStream())
    {
        collection.Write(ms, webpSettings);
        return ms.ToArray();
    }
}

The image below shows the actual results in WhatsApp. I originally had four stickers, but only three are displayed. The missing one is the test image that I emailed to you. 图片

JimBobSquarePants commented 3 days ago

The webp output is 100% valid according to every test I can apply including official tools from Google themselves. I would use webpinfo to compare blend/disposal properties to see if updating those to match the Magick version changes anything.

Dylan-Jinx commented 3 days ago

I checked the information on macOS, and the previews in Firefox, Safari, and Chrome all displayed correctly.

However, since Magick.NET can convert the file successfully and it passes verification in WhatsApp, I wonder if there might be some differences in the information.

I attached the webp file that works after conversion with Magick.NET (named "test.webp") from the help system you provided yesterday; it may be necessary to compare it with the webp generated by ImageSharp to identify any discrepancies.

JimBobSquarePants commented 3 days ago

That’s what I was asking you to do via webpinfo.

Dylan-Jinx commented 3 days ago

I followed your suggestion and used webpinfo to analyze the two WEBP files. test.webp is working fine, while testImageSharp.webp has issues when imported into WhatsApp. The webinfo results are as follows.

RIFF HEADER:
File:test.webp
  File size: 162746
Chunk VP8X at offset     12, length     18
  ICCP: 0
  Alpha: 1
  EXIF: 0
  XMP: 0
  Animation: 1
  Canvas size 512 x 512
Chunk ANIM at offset     30, length     14
  Background color:(ARGB) ff ff ff ff
  Loop count      : 0
Chunk ANMF at offset     44, length  36100
  Offset_X: 0
  Offset_Y: 0
  Width: 512
  Height: 512
  Duration: 100
  Dispose: 0
  Blend: 1
Chunk VP8L at offset     68, length  36076
  Width: 512
  Height: 512
  Alpha: 1
  Animation: 0
  Format: Lossless (2)
Chunk ANMF at offset  36144, length  31776
  Offset_X: 60
  Offset_Y: 40
  Width: 428
  Height: 418
  Duration: 100
  Dispose: 0
  Blend: 1
Chunk VP8L at offset  36168, length  31752
  Width: 428
  Height: 418
  Alpha: 1
  Animation: 0
  Format: Lossless (2)
Chunk ANMF at offset  67920, length  32718
  Offset_X: 60
  Offset_Y: 28
  Width: 427
  Height: 431
  Duration: 100
  Dispose: 0
  Blend: 1
Chunk VP8L at offset  67944, length  32694
  Width: 427
  Height: 431
  Alpha: 1
  Animation: 0
  Format: Lossless (2)
Chunk ANMF at offset 100638, length  29190
  Offset_X: 62
  Offset_Y: 28
  Width: 425
  Height: 431
  Duration: 100
  Dispose: 0
  Blend: 1
Chunk VP8L at offset 100662, length  29166
  Width: 425
  Height: 431
  Alpha: 1
  Animation: 0
  Format: Lossless (2)
Chunk ANMF at offset 129828, length  32918
  Offset_X: 64
  Offset_Y: 68
  Width: 431
  Height: 392
  Duration: 100
  Dispose: 0
  Blend: 1
Chunk VP8L at offset 129852, length  32894
  Width: 431
  Height: 392
  Alpha: 1
  Animation: 0
  Format: Lossless (2)
No error detected.
File: testImageSharp.webp
RIFF HEADER:
  File size: 168496
Chunk VP8X at offset     12, length     18
  ICCP: 0
  Alpha: 1
  EXIF: 0
  XMP: 1
  Animation: 1
  Canvas size 512 x 512
Chunk ANIM at offset     30, length     14
  Background color:(ARGB) 00 00 00 00
  Loop count      : 0
Chunk ANMF at offset     44, length  36100
  Offset_X: 0
  Offset_Y: 0
  Width: 512
  Height: 512
  Duration: 100
  Dispose: 1
  Blend: 1
Chunk VP8L at offset     68, length  36076
  Width: 512
  Height: 512
  Alpha: 1
  Animation: 0
  Format: Lossless (2)
Chunk ANMF at offset  36144, length  32922
  Offset_X: 28
  Offset_Y: 24
  Width: 457
  Height: 461
  Duration: 0
  Dispose: 1
  Blend: 1
Chunk VP8L at offset  36168, length  32898
  Width: 457
  Height: 461
  Alpha: 1
  Animation: 0
  Format: Lossless (2)
Chunk ANMF at offset  69066, length  33782
  Offset_X: 28
  Offset_Y: 24
  Width: 459
  Height: 461
  Duration: 0
  Dispose: 1
  Blend: 1
Chunk VP8L at offset  69090, length  33758
  Width: 459
  Height: 461
  Alpha: 1
  Animation: 0
  Format: Lossless (2)
Chunk ANMF at offset 102848, length  30156
  Offset_X: 28
  Offset_Y: 24
  Width: 458
  Height: 461
  Duration: 0
  Dispose: 1
  Blend: 1
Chunk VP8L at offset 102872, length  30132
  Width: 458
  Height: 461
  Alpha: 1
  Animation: 0
  Format: Lossless (2)
Chunk ANMF at offset 133004, length  34604
  Offset_X: 28
  Offset_Y: 24
  Width: 467
  Height: 461
  Duration: 0
  Dispose: 0
  Blend: 0
Chunk VP8L at offset 133028, length  34580
  Width: 467
  Height: 461
  Alpha: 1
  Animation: 0
  Format: Lossless (2)
Chunk XMP  at offset 167608, length    888
No error detected.

Conclusion: Based on the webpinfo output for these two files, here are the key differences:

1. XMP Data:
    test.webp: No XMP data.
    testImageSharp.webp: Contains XMP data. This data may affect how some applications parse and display the image, but it usually doesn't lead to compatibility issues.

2. Animation:
    Both files support animation, but in testImageSharp.webp, some frames have a Dispose value of 1 (which means clear the previous frame), while test.webp has a Dispose value of 0. This could affect how the animation performs.

3. Dimensions:
    Both files have similar width and height data, but in testImageSharp.webp, certain frames' Offset_X and Offset_Y values might cause display issues in WhatsApp.

4. Background Color:
    test.webp uses a white background (ARGB: ff ff ff ff), while testImageSharp.webp has a transparent background (ARGB: 00 00 00 00). A transparent background may cause display problems in certain environments.
JimBobSquarePants commented 3 days ago

I would suggest stripping out the XMP data.

I would avoid conjecture when describing the differences. Things like offset and background color are perfectly valid.

Dylan-Jinx commented 3 days ago
using Image gifImage = Image.Load(image);

// Manually copy the properties across.
GifMetadata gMeta = gifImage.Metadata.GetGifMetadata();
WebpMetadata wMeta = gifImage.Metadata.GetWebpMetadata();
Console.WriteLine(wMeta);
wMeta.FileFormat = WebpFileFormatType.Lossless;
wMeta.RepeatCount = gMeta.RepeatCount;
wMeta.BackgroundColor = Color.White;

gifImage.Metadata.XmpProfile = null;

foreach (var frame in gifImage.Frames)
{
    var webpFrame = frame.Metadata.GetWebpMetadata();
    webpFrame.BlendMethod = WebpBlendMethod.Source;
    webpFrame.DisposalMethod = WebpDisposalMethod.DoNotDispose;
    webpFrame.FrameDelay = 100;
}

gifImage.SaveAsWebp(Path.Combine(AppContext.BaseDirectory, "202410231720.webp"));

After repeated comparisons, I followed your instructions this time and removed the XMP data, but it still didn't work. So, I gradually adjusted the properties to match the webpinfo results of the working image, but it still didn’t help.

It wasn't until I added the following statement that it was finally recognized correctly, and I was really excited by the result.😯

webpFrame.FrameDelay = 100;

After comparison, I found that the images generated by Magick.Net have a Duration of 100 for each ANMF in the webpinfo, while ImageSharp's Duration is set to 0.

WhatsApp may use the Duration for its judgment, which could lead to the images generated by ImageSharp being filtered out. I think that ImageSharp should correctly set the Duration property for each ANMF in the WebP metadata when using SaveAsWebp.

Dylan-Jinx commented 3 days ago

Should ImageSharp handle similar special cases? If possible, I would like to assist in completing this great work.

JimBobSquarePants commented 3 days ago

NICE DETECTIVE WORK!! 🕵️

I think that ImageSharp should correctly set the Duration property for each ANMF in the WebP metadata when using SaveAsWebp.

ImageSharp is actually doing the correct thing here by performing an exact translation.

If you look at the GIF metadata your will see that only the first frame actually has a frame delay set - All the others are zero. (Verified via 3rd party)

There's no rule for the minimum frame delay in either specification (nor also APNG) but browsers and other tools tend to use a minimum value for display purposes.

I've done some research in the past though for various browsers and here are my findings:

Minimum Frame Delay for Animated Formats in Various Browsers

Format Chrome Firefox Safari Edge Opera
GIF 10 ms (enforced) 20 ms (enforced) 20 ms (enforced) 10 ms (enforced) 10 ms (enforced)
APNG 10 ms (enforced) 10 ms (native) 10 ms (native) 10 ms (native) 10 ms (native)
WEBP 16.67 ms (60 FPS) 16.67 ms 16.67 ms 16.67 ms 16.67 ms

Key Details:

  1. GIF:

    • In older browsers, delays below 20 ms are often treated as 100 ms or 10 ms due to the way timers are handled.
    • Modern browsers typically enforce a minimum delay of around 10 ms to 20 ms.
  2. APNG (Animated PNG):

    • Modern browsers have a better-optimized frame delay for APNG, with 10 ms being a typical enforced minimum.
  3. WEBP:

    • WEBP enforces a 16.67 ms frame delay, effectively limiting animations to a max of 60 frames per second (FPS).

As you can see the minimum display value is inconsistent for GIF files and does not match the default set by Magick.Net.

While their approach is a sensible one, I disagree that it is the correct direction for ImageSharp. We should not change metadata without explicit instruction to do so.

Dylan-Jinx commented 3 days ago

NICE JOB! 👍 A very detailed explanation!

I share the same attitude as you, believing that metadata should not be changed arbitrarily. I think this issue can help colleagues in the future address similar situations more elegantly.