Skip to content

Commit f3ff11e

Browse files
Handle dedup of local palette of 256 length
1 parent 07fd936 commit f3ff11e

File tree

5 files changed

+48
-13
lines changed

5 files changed

+48
-13
lines changed

src/ImageSharp/Formats/Gif/GifEncoderCore.cs

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -380,18 +380,42 @@ private IndexedImageFrame<TPixel> QuantizeAdditionalFrameAndUpdateMetadata<TPixe
380380
// We can use the color data from the decoded metadata here.
381381
// We avoid dithering by default to preserve the original colors.
382382
ReadOnlyMemory<Color> palette = metadata.LocalColorTable.Value;
383-
384383
if (hasDuplicates && !metadata.HasTransparency)
385384
{
386-
// A difference was captured but the metadata does not have transparency.
385+
// Duplicates were captured but the metadata does not have transparency.
387386
metadata.HasTransparency = true;
388-
transparencyIndex = palette.Length;
389-
metadata.TransparencyIndex = ClampIndex(transparencyIndex);
390-
}
391387

392-
PaletteQuantizer quantizer = new(palette, new() { Dither = null }, transparencyIndex);
393-
using IQuantizer<TPixel> frameQuantizer = quantizer.CreatePixelSpecificQuantizer<TPixel>(this.configuration, quantizer.Options);
394-
quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(encodingFrame, bounds);
388+
if (palette.Length < 256)
389+
{
390+
// We can use the existing palette and set the transparent index as the length.
391+
// decoders will ignore this value.
392+
transparencyIndex = palette.Length;
393+
metadata.TransparencyIndex = ClampIndex(transparencyIndex);
394+
395+
PaletteQuantizer quantizer = new(palette, new() { Dither = null }, transparencyIndex);
396+
using IQuantizer<TPixel> frameQuantizer = quantizer.CreatePixelSpecificQuantizer<TPixel>(this.configuration, quantizer.Options);
397+
quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(encodingFrame, bounds);
398+
}
399+
else
400+
{
401+
// We must quantize the frame to generate a local color table.
402+
IQuantizer quantizer = this.hasQuantizer ? this.quantizer! : KnownQuantizers.Octree;
403+
using IQuantizer<TPixel> frameQuantizer = quantizer.CreatePixelSpecificQuantizer<TPixel>(this.configuration, quantizer.Options);
404+
quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(encodingFrame, bounds);
405+
406+
// The transparency index derived by the quantizer will differ from the index
407+
// within the metadata. We need to update the metadata to reflect this.
408+
int derivedTransparencyIndex = GetTransparentIndex(quantized, null);
409+
metadata.TransparencyIndex = ClampIndex(derivedTransparencyIndex);
410+
}
411+
}
412+
else
413+
{
414+
// Just use the local palette.
415+
PaletteQuantizer quantizer = new(palette, new() { Dither = null }, transparencyIndex);
416+
using IQuantizer<TPixel> frameQuantizer = quantizer.CreatePixelSpecificQuantizer<TPixel>(this.configuration, quantizer.Options);
417+
quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(encodingFrame, bounds);
418+
}
395419
}
396420
else
397421
{

src/ImageSharp/Formats/Gif/MetadataExtensions.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,14 +77,20 @@ internal static AnimatedImageMetadata ToAnimatedImageMetadata(this GifMetadata s
7777
}
7878

7979
internal static AnimatedImageFrameMetadata ToAnimatedImageFrameMetadata(this GifFrameMetadata source)
80-
=> new()
80+
{
81+
// For most scenarios we would consider the blend method to be 'Over' however if a frame has a disposal method of 'RestoreToBackground' or
82+
// has a local palette with 256 colors and is not transparent we should use 'Source'.
83+
bool blendSource = source.DisposalMethod == GifDisposalMethod.RestoreToBackground || (source.LocalColorTable?.Length == 256 && !source.HasTransparency);
84+
85+
return new()
8186
{
8287
ColorTable = source.LocalColorTable,
8388
ColorTableMode = source.ColorTableMode == GifColorTableMode.Global ? FrameColorTableMode.Global : FrameColorTableMode.Local,
8489
Duration = TimeSpan.FromMilliseconds(source.FrameDelay * 10),
8590
DisposalMode = GetMode(source.DisposalMethod),
86-
BlendMode = source.DisposalMethod == GifDisposalMethod.RestoreToBackground ? FrameBlendMode.Source : FrameBlendMode.Over,
91+
BlendMode = blendSource ? FrameBlendMode.Source : FrameBlendMode.Over,
8792
};
93+
}
8894

8995
private static FrameDisposalMode GetMode(GifDisposalMethod method) => method switch
9096
{

src/ImageSharp/Formats/Webp/BitReader/Vp8LBitReader.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public Vp8LBitReader(IMemoryOwner<byte> data)
7171
this.Eos = false;
7272

7373
ulong currentValue = 0;
74-
System.Span<byte> dataSpan = this.Data.Memory.Span;
74+
Span<byte> dataSpan = this.Data.Memory.Span;
7575
for (int i = 0; i < 8; i++)
7676
{
7777
currentValue |= (ulong)dataSpan[i] << (8 * i);
@@ -103,7 +103,7 @@ public Vp8LBitReader(Stream inputStream, uint imageDataSize, MemoryAllocator mem
103103
}
104104

105105
ulong currentValue = 0;
106-
System.Span<byte> dataSpan = this.Data.Memory.Span;
106+
Span<byte> dataSpan = this.Data.Memory.Span;
107107
for (int i = 0; i < length; i++)
108108
{
109109
currentValue |= (ulong)dataSpan[i] << (8 * i);

tests/ImageSharp.Tests/TestImages.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,7 @@ public static class Gif
487487
public const string GlobalQuantizationTest = "Gif/GlobalQuantizationTest.gif";
488488
public const string MixedDisposal = "Gif/mixed-disposal.gif";
489489
public const string M4nb = "Gif/m4nb.gif";
490+
public const string Bit18RGBCube = "Gif/18-bit_RGB_Cube.gif";
490491

491492
// Test images from https://github.com/robert-ancell/pygif/tree/master/test-suite
492493
public const string ZeroSize = "Gif/image-zero-size.gif";
@@ -533,7 +534,8 @@ public static class Issues
533534
Issues.Issue2450_A,
534535
Issues.Issue2450_B,
535536
Issues.BadDescriptorWidth,
536-
Issues.Issue1530
537+
Issues.Issue1530,
538+
Bit18RGBCube
537539
};
538540
}
539541

Lines changed: 3 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)