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

Error block in image result after saving after loading some files #2866

Open
4 tasks done
BAKAOLC opened this issue Jan 22, 2025 · 9 comments
Open
4 tasks done

Error block in image result after saving after loading some files #2866

BAKAOLC opened this issue Jan 22, 2025 · 9 comments

Comments

@BAKAOLC
Copy link

BAKAOLC commented Jan 22, 2025

Prerequisites

  • I have written a descriptive issue title
  • I have verified that I am running the latest version of ImageSharp
  • I have verified if the problem exist in both DEBUG and RELEASE mode
  • I have searched open and closed issues to ensure it has not already been reported

ImageSharp version

3.1.6

Other ImageSharp packages and versions

SixLabors.ImageSharp.Drawing 2.1.5

Environment (Operating system, version and so on)

macOS Sequoia 15.2

.NET Framework version

.net 9.0

Description

There are some specific compressed Gif files that are saved after being loaded that lose their differential rendering properties, resulting in black blocks in the image. But the original image renders fine without ImageSharp. However the image works fine in ImageSharp after it has been re-encoded by other software.

The result of the error looks like this:
2.gif

As well, how do I get the full frame image of the compressed Gif correctly in the code instead of the difference?

Steps to Reproduce

just load and save it.

using var image = await Image.LoadAsync<Rgba32>("1.gif");
await image.SaveAsync("2.gif");

Images

1.gif

@JimBobSquarePants
Copy link
Member

JimBobSquarePants commented Jan 22, 2025

What do you mean by compressed gifs?

The encoder only encodes the difference because otherwise you end up with massive gifs. There’s no way to turn that off.

@BAKAOLC
Copy link
Author

BAKAOLC commented Jan 24, 2025

Sorry for the long delay in replying.

What do you mean by compressed gifs?

The encoder only encodes the difference because otherwise you end up with massive gifs. There’s no way to turn that off.

I'm referring to gifs that only keep a record of the changed pixels.
Since the images themselves do not originate from me, I cannot confirm exactly what was used for gif optimization.

After I ran a lot of tests, I figured out why I was asking the question. Using CloneFrame itself gives me the results I want, and the reason I'm asking is because I usually save as a png or gif, so it's getting the wrong results, and leading me to think that what this method gets would be the difference pixel map (instead of the full frame image)

This gif if I export it using CloneFrame and save it as a png, it will all show up as a pure empty image.

using var image = await Image.LoadAsync<Rgba32>("1.gif");
for (var i = 0; i < image.Frames.Count; i++)
{
    using var frameImage = image.Frames.CloneFrame(i);
    frameImage.Save($"frame-{i}.png");
}

Image

And if I store it in jpg format, it's correct, this image is the result I want.

using var image = await Image.LoadAsync<Rgba32>("1.gif");
for (var i = 0; i < image.Frames.Count; i++)
{
    using var frameImage = image.Frames.CloneFrame(i);
    frameImage.Save($"frame-{i}.jpg");
}

Image

@BAKAOLC BAKAOLC changed the title The specific Gif file is loaded and saved as the wrong style. Error block in image result after saving after loading some files Jan 26, 2025
@BAKAOLC
Copy link
Author

BAKAOLC commented Jan 26, 2025

A similar phenomenon occurs in webp. For example, these two images.

using var image = await Image.LoadAsync<Rgba32>("1.webp");
await image.SaveAsync("2.webp");

Image

images.zip

I changed the title of the issue because of this.

@JimBobSquarePants
Copy link
Member

@BAKAOLC I think your issue is this.

From the spec:

  • The background color MAY contain a non-opaque alpha value, even if the Alpha flag in the 'VP8X' Chunk is unset.
  • Viewer applications SHOULD treat the background color value as a hint and are not required to use it.

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

ImageSharp has a dedicated decoder option when dealing with this ambiguity but requires explicit usage of the decoder to use it.

using FileStream stream = File.OpenRead("1.webp");
WebpDecoderOptions decoderOptions = new() { GeneralOptions = this.options, BackgroundColorHandling = BackgroundColorHandling.Ignore };
using WebpDecoderCore decoder = new(decoderOptions);
using Image<Rgb32> image = decoder.Decode<Rgb32>(decoderOptions.GeneralOptions.Configuration, stream, cancellationToken);
image.Save("2.webp");

This gives you your output without the blocks.

I'm almost tempted to use Ignore as the default because that's what browsers seem to do.

However, I have found something odd in the output. There's some ghosting on frame 16 where there are remnants of an ear that shouldn't be there.

Image

If I compare that frame to one extracted using EazyGif you will see that the BGR components are the same however the alpha component has a value. That value is coming straight out of the AlphaDecoder.

Image

Is it a bug.... I don't know, is there some unspecified magic that is performed by other decoders.... I don't know. The AlphaDecoder is complex as all hell. @brianpopow I'll need your help here...

@BAKAOLC
Copy link
Author

BAKAOLC commented Feb 4, 2025

ImageSharp has a dedicated decoder option when dealing with this ambiguity but requires explicit usage of the decoder to use it.

Thank you very much for your answer, it solves part of my problem.

This gif if I export it using CloneFrame and save it as a png, it will all show up as a pure empty image.

After additional experimentation, I confirmed that the gif will export the png image correctly using the following code. This issue is still one of the more confusing points for me at the moment.

var image = await Image.LoadAsync<Rgba32>("1.gif").ConfigureAwait(false);
for (var i = 0; i < image.Frames.Count; i++)
{
    using var frameImage = image.Frames.CloneFrame(i);
    frameImage.Metadata.GetGifMetadata().GlobalColorTable = null;
    await frameImage.SaveAsync($"frame-{i}.png").ConfigureAwait(false);
}

Image

This doesn't happen with all gif files extracting png frames. And by outputting a version of the empty file, does that mean it tries to store it as APNG?

Also I don't think extracted frames should have this particular discrepancy when stored directly as a single frame image, should this behavior be handled away by default?

@BAKAOLC
Copy link
Author

BAKAOLC commented Feb 4, 2025

var image = await Image.LoadAsync<Rgba32>("1.gif").ConfigureAwait(false);
image.Metadata.GetGifMetadata().GlobalColorTable = null;
await image.SaveAsync("2.gif").ConfigureAwait(false);

Again, no unusual black blocks will appear when using this method when saving immediately after a direct read. But this should mean that there is something wrong with the GlobalColorTable data, because as I thought, without any editing of the image, the generated result should not be noticeably different from the original.

@JimBobSquarePants
Copy link
Member

JimBobSquarePants commented Feb 5, 2025

OK... So I had a good look at the GIF. It's an odd one.

It has a global color table but only two entries within that table. None of the frames actually use that table.

ImageSharp assumes that the table use both valid so when converting between formats will attempt to use it. It also attempts to use it for the first frame when reencoding the GIF. I'm going to have to revisit the logic to handle such oddities.

@JimBobSquarePants
Copy link
Member

Figured out the WebP ghosting issue! We were disposing of the background restore area based on the current frame's disposal mode not the previous one! (@brianpopow crisis averted).

Saved output attached (GitHub does not allow WebP)
WebpDecoder_CanDecode_Issue2866_Rgba32_Issue2866.zip

@BAKAOLC I'll open a PR tomorrow.

@brianpopow
Copy link
Collaborator

@JimBobSquarePants I am glad you found this issue, this was hard to spot!

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

No branches or pull requests

3 participants