Skip to content

Commit 5d77de9

Browse files
Fix PNG animation encoding and quantizer output
1 parent 386e17d commit 5d77de9

16 files changed

+567
-272
lines changed

src/ImageSharp/Formats/Gif/GifDecoderCore.cs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ protected override Image<TPixel> Decode<TPixel>(BufferedReadStream stream, Cance
112112
Image<TPixel>? image = null;
113113
ImageFrame<TPixel>? previousFrame = null;
114114
GifDisposalMethod? previousDisposalMethod = null;
115+
bool globalColorTableUsed = false;
115116

116117
try
117118
{
@@ -129,7 +130,7 @@ protected override Image<TPixel> Decode<TPixel>(BufferedReadStream stream, Cance
129130
break;
130131
}
131132

132-
this.ReadFrame(stream, ref image, ref previousFrame, ref previousDisposalMethod, backgroundPixel);
133+
globalColorTableUsed |= this.ReadFrame(stream, ref image, ref previousFrame, ref previousDisposalMethod, backgroundPixel);
133134

134135
// Reset per-frame state.
135136
this.imageDescriptor = default;
@@ -164,6 +165,13 @@ protected override Image<TPixel> Decode<TPixel>(BufferedReadStream stream, Cance
164165
break;
165166
}
166167
}
168+
169+
// We cannot always trust the global GIF palette has actually been used.
170+
// https://github.com/SixLabors/ImageSharp/issues/2866
171+
if (!globalColorTableUsed)
172+
{
173+
this.gifMetadata.ColorTableMode = GifColorTableMode.Local;
174+
}
167175
}
168176
finally
169177
{
@@ -425,7 +433,7 @@ private void ReadComments(BufferedReadStream stream)
425433
/// <param name="previousFrame">The previous frame.</param>
426434
/// <param name="previousDisposalMethod">The previous disposal method.</param>
427435
/// <param name="backgroundPixel">The background color pixel.</param>
428-
private void ReadFrame<TPixel>(
436+
private bool ReadFrame<TPixel>(
429437
BufferedReadStream stream,
430438
ref Image<TPixel>? image,
431439
ref ImageFrame<TPixel>? previousFrame,
@@ -461,6 +469,8 @@ private void ReadFrame<TPixel>(
461469

462470
// Skip any remaining blocks
463471
SkipBlock(stream);
472+
473+
return !hasLocalColorTable;
464474
}
465475

466476
/// <summary>
@@ -809,6 +819,7 @@ private void ReadLogicalScreenDescriptorAndGlobalColorTable(BufferedReadStream s
809819
if (table is not null && index < table.Value.Length)
810820
{
811821
this.backgroundColor = table.Value.Span[index];
822+
this.gifMetadata.BackgroundColorIndex = index;
812823
}
813824
else
814825
{

src/ImageSharp/Formats/Gif/MetadataExtensions.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,15 +60,20 @@ public static bool TryGetGifMetadata(this ImageFrameMetadata source, [NotNullWhe
6060
=> source.TryGetFormatMetadata(GifFormat.Instance, out metadata);
6161

6262
internal static AnimatedImageMetadata ToAnimatedImageMetadata(this GifMetadata source)
63+
{
64+
bool global = source.ColorTableMode == GifColorTableMode.Global;
65+
Color color = global && source.GlobalColorTable.HasValue && source.GlobalColorTable.Value.Span.Length > source.BackgroundColorIndex
66+
? source.GlobalColorTable.Value.Span[source.BackgroundColorIndex]
67+
: Color.Transparent;
6368

64-
// We cannot trust the global GIF palette so don't use it.
65-
// https://github.com/SixLabors/ImageSharp/issues/2866
66-
=> new()
69+
return new()
6770
{
6871
ColorTableMode = source.ColorTableMode == GifColorTableMode.Global ? FrameColorTableMode.Global : FrameColorTableMode.Local,
72+
ColorTable = global ? source.GlobalColorTable : null,
6973
RepeatCount = source.RepeatCount,
70-
BackgroundColor = Color.Transparent,
74+
BackgroundColor = color,
7175
};
76+
}
7277

7378
internal static AnimatedImageFrameMetadata ToAnimatedImageFrameMetadata(this GifFrameMetadata source)
7479
{

src/ImageSharp/Formats/Png/PngEncoder.cs

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,13 @@
22
// Licensed under the Six Labors Split License.
33
#nullable disable
44

5-
using SixLabors.ImageSharp.Processing.Processors.Quantization;
6-
75
namespace SixLabors.ImageSharp.Formats.Png;
86

97
/// <summary>
108
/// Image encoder for writing image data to a stream in png format.
119
/// </summary>
1210
public class PngEncoder : QuantizingImageEncoder
1311
{
14-
/// <summary>
15-
/// Initializes a new instance of the <see cref="PngEncoder"/> class.
16-
/// </summary>
17-
public PngEncoder()
18-
19-
// Hack. TODO: Investigate means to fix/optimize the Wu quantizer.
20-
// The Wu quantizer does not handle the default sampling strategy well for some larger images.
21-
// It's expensive and the results are not better than the extensive strategy.
22-
=> this.PixelSamplingStrategy = new ExtensivePixelSamplingStrategy();
23-
2412
/// <summary>
2513
/// Gets the number of bits per sample or per palette index (not per pixel).
2614
/// Not all values are allowed for all <see cref="ColorType" /> values.

0 commit comments

Comments
 (0)