Browse Source

Make color tables mutable

pull/2500/head
James Jackson-South 3 years ago
parent
commit
43aaad1d49
  1. 4
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  2. 8
      src/ImageSharp/Formats/Gif/GifEncoderCore.cs
  3. 8
      src/ImageSharp/Formats/Gif/GifFrameMetadata.cs
  4. 10
      src/ImageSharp/Formats/Gif/GifMetadata.cs
  5. 10
      tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs
  6. 8
      tests/ImageSharp.Tests/Formats/Gif/GifFrameMetadataTests.cs
  7. 10
      tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs
  8. 2
      tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.NonGeneric.cs
  9. 4
      tests/ImageSharp.Tests/Metadata/ImageFrameMetadataTests.cs
  10. 2
      tests/ImageSharp.Tests/TestImages.cs

4
src/ImageSharp/Formats/Gif/GifDecoderCore.cs

@ -716,7 +716,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
colorTable[i] = new Color(Unsafe.Add(ref localBase, (uint)i)); colorTable[i] = new Color(Unsafe.Add(ref localBase, (uint)i));
} }
gifMeta.DecodedLocalColorTable = colorTable; gifMeta.LocalColorTable = colorTable;
} }
// Graphics control extensions is optional. // Graphics control extensions is optional.
@ -793,7 +793,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
colorTable[i] = new Color(Unsafe.Add(ref globalBase, (uint)i)); colorTable[i] = new Color(Unsafe.Add(ref globalBase, (uint)i));
} }
this.gifMetadata.DecodedGlobalColorTable = colorTable; this.gifMetadata.GlobalColorTable = colorTable;
} }
} }
} }

8
src/ImageSharp/Formats/Gif/GifEncoderCore.cs

@ -102,10 +102,10 @@ internal sealed class GifEncoderCore : IImageEncoderInternals
if (this.quantizer is null) if (this.quantizer is null)
{ {
// Is this a gif with color information. If so use that, otherwise use octree. // Is this a gif with color information. If so use that, otherwise use octree.
if (gifMetadata.ColorTableMode == GifColorTableMode.Global && gifMetadata.DecodedGlobalColorTable.Length > 0) if (gifMetadata.ColorTableMode == GifColorTableMode.Global && gifMetadata.GlobalColorTable?.Length > 0)
{ {
// We avoid dithering by default to preserve the original colors. // We avoid dithering by default to preserve the original colors.
this.quantizer = new PaletteQuantizer(gifMetadata.DecodedGlobalColorTable, new() { Dither = null }); this.quantizer = new PaletteQuantizer(gifMetadata.GlobalColorTable.Value, new() { Dither = null });
} }
else else
{ {
@ -234,11 +234,11 @@ internal sealed class GifEncoderCore : IImageEncoderInternals
if (useLocal) if (useLocal)
{ {
// Reassign using the current frame and details. // Reassign using the current frame and details.
if (metadata?.DecodedLocalColorTable.Length > 0) if (metadata?.LocalColorTable?.Length > 0)
{ {
// We can use the color data from the decoded metadata here. // We can use the color data from the decoded metadata here.
// We avoid dithering by default to preserve the original colors. // We avoid dithering by default to preserve the original colors.
PaletteQuantizer localQuantizer = new(metadata.DecodedLocalColorTable, new() { Dither = null }); PaletteQuantizer localQuantizer = new(metadata.LocalColorTable.Value, new() { Dither = null });
using IQuantizer<TPixel> frameQuantizer = localQuantizer.CreatePixelSpecificQuantizer<TPixel>(this.configuration, localQuantizer.Options); using IQuantizer<TPixel> frameQuantizer = localQuantizer.CreatePixelSpecificQuantizer<TPixel>(this.configuration, localQuantizer.Options);
quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(frame, frame.Bounds()); quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(frame, frame.Bounds());
} }

8
src/ImageSharp/Formats/Gif/GifFrameMetadata.cs

@ -25,9 +25,9 @@ public class GifFrameMetadata : IDeepCloneable
this.FrameDelay = other.FrameDelay; this.FrameDelay = other.FrameDelay;
this.DisposalMethod = other.DisposalMethod; this.DisposalMethod = other.DisposalMethod;
if (other.DecodedLocalColorTable.Length > 0) if (other.LocalColorTable?.Length > 0)
{ {
this.DecodedLocalColorTable = other.DecodedLocalColorTable.ToArray(); this.LocalColorTable = other.LocalColorTable.Value.ToArray();
} }
this.HasTransparency = other.HasTransparency; this.HasTransparency = other.HasTransparency;
@ -40,9 +40,9 @@ public class GifFrameMetadata : IDeepCloneable
public GifColorTableMode ColorTableMode { get; set; } public GifColorTableMode ColorTableMode { get; set; }
/// <summary> /// <summary>
/// Gets the decoded global color table, if any. /// Gets or sets the local color table, if any.
/// </summary> /// </summary>
public ReadOnlyMemory<Color> DecodedLocalColorTable { get; internal set; } public ReadOnlyMemory<Color>? LocalColorTable { get; set; }
/// <summary> /// <summary>
/// Gets or sets a value indicating whether the frame has transparency /// Gets or sets a value indicating whether the frame has transparency

10
src/ImageSharp/Formats/Gif/GifMetadata.cs

@ -25,9 +25,9 @@ public class GifMetadata : IDeepCloneable
this.ColorTableMode = other.ColorTableMode; this.ColorTableMode = other.ColorTableMode;
this.BackgroundColor = other.BackgroundColor; this.BackgroundColor = other.BackgroundColor;
if (other.DecodedGlobalColorTable.Length > 0) if (other.GlobalColorTable?.Length > 0)
{ {
this.DecodedGlobalColorTable = other.DecodedGlobalColorTable.ToArray(); this.GlobalColorTable = other.GlobalColorTable.Value.ToArray();
} }
for (int i = 0; i < other.Comments.Count; i++) for (int i = 0; i < other.Comments.Count; i++)
@ -50,12 +50,12 @@ public class GifMetadata : IDeepCloneable
public GifColorTableMode ColorTableMode { get; set; } public GifColorTableMode ColorTableMode { get; set; }
/// <summary> /// <summary>
/// Gets the decoded global color table, if any. /// Gets or sets the global color table, if any.
/// </summary> /// </summary>
public ReadOnlyMemory<Color> DecodedGlobalColorTable { get; internal set; } public ReadOnlyMemory<Color>? GlobalColorTable { get; set; }
/// <summary> /// <summary>
/// Gets or sets the index at the <see cref="DecodedGlobalColorTable"/> for the background color. /// Gets or sets the index at the <see cref="GlobalColorTable"/> for the background color.
/// The background color is the color used for those pixels on the screen that are not covered by an image. /// The background color is the color used for those pixels on the screen that are not covered by an image.
/// </summary> /// </summary>
public byte BackgroundColor { get; set; } public byte BackgroundColor { get; set; }

10
tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs

@ -175,11 +175,11 @@ public class GifEncoderTests
int maxColors; int maxColors;
if (colorMode == GifColorTableMode.Global) if (colorMode == GifColorTableMode.Global)
{ {
maxColors = metaData.DecodedGlobalColorTable.Length; maxColors = metaData.GlobalColorTable.Value.Length;
} }
else else
{ {
maxColors = frameMetadata.DecodedLocalColorTable.Length; maxColors = frameMetadata.LocalColorTable.Value.Length;
} }
GifEncoder encoder = new() GifEncoder encoder = new()
@ -201,11 +201,11 @@ public class GifEncoderTests
colorMode = cloneMetadata.ColorTableMode; colorMode = cloneMetadata.ColorTableMode;
if (colorMode == GifColorTableMode.Global) if (colorMode == GifColorTableMode.Global)
{ {
maxColors = metaData.DecodedGlobalColorTable.Length; maxColors = metaData.GlobalColorTable.Value.Length;
} }
else else
{ {
maxColors = frameMetadata.DecodedLocalColorTable.Length; maxColors = frameMetadata.LocalColorTable.Value.Length;
} }
Assert.Equal(64, maxColors); Assert.Equal(64, maxColors);
@ -217,7 +217,7 @@ public class GifEncoderTests
if (iMeta.ColorTableMode == GifColorTableMode.Local) if (iMeta.ColorTableMode == GifColorTableMode.Local)
{ {
Assert.Equal(iMeta.DecodedLocalColorTable.Length, cMeta.DecodedLocalColorTable.Length); Assert.Equal(iMeta.LocalColorTable.Value.Length, cMeta.LocalColorTable.Value.Length);
} }
Assert.Equal(iMeta.FrameDelay, cMeta.FrameDelay); Assert.Equal(iMeta.FrameDelay, cMeta.FrameDelay);

8
tests/ImageSharp.Tests/Formats/Gif/GifFrameMetadataTests.cs

@ -15,18 +15,18 @@ public class GifFrameMetadataTests
{ {
FrameDelay = 1, FrameDelay = 1,
DisposalMethod = GifDisposalMethod.RestoreToBackground, DisposalMethod = GifDisposalMethod.RestoreToBackground,
DecodedLocalColorTable = new[] { Color.Black, Color.White } LocalColorTable = new[] { Color.Black, Color.White }
}; };
GifFrameMetadata clone = (GifFrameMetadata)meta.DeepClone(); GifFrameMetadata clone = (GifFrameMetadata)meta.DeepClone();
clone.FrameDelay = 2; clone.FrameDelay = 2;
clone.DisposalMethod = GifDisposalMethod.RestoreToPrevious; clone.DisposalMethod = GifDisposalMethod.RestoreToPrevious;
clone.DecodedLocalColorTable = new[] { Color.Black }; clone.LocalColorTable = new[] { Color.Black };
Assert.False(meta.FrameDelay.Equals(clone.FrameDelay)); Assert.False(meta.FrameDelay.Equals(clone.FrameDelay));
Assert.False(meta.DisposalMethod.Equals(clone.DisposalMethod)); Assert.False(meta.DisposalMethod.Equals(clone.DisposalMethod));
Assert.False(meta.DecodedLocalColorTable.Length == clone.DecodedLocalColorTable.Length); Assert.False(meta.LocalColorTable.Value.Length == clone.LocalColorTable.Value.Length);
Assert.Equal(1, clone.DecodedLocalColorTable.Length); Assert.Equal(1, clone.LocalColorTable.Value.Length);
} }
} }

10
tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs

@ -34,7 +34,7 @@ public class GifMetadataTests
{ {
RepeatCount = 1, RepeatCount = 1,
ColorTableMode = GifColorTableMode.Global, ColorTableMode = GifColorTableMode.Global,
DecodedGlobalColorTable = new[] { Color.Black, Color.White }, GlobalColorTable = new[] { Color.Black, Color.White },
Comments = new List<string> { "Foo" } Comments = new List<string> { "Foo" }
}; };
@ -42,12 +42,12 @@ public class GifMetadataTests
clone.RepeatCount = 2; clone.RepeatCount = 2;
clone.ColorTableMode = GifColorTableMode.Local; clone.ColorTableMode = GifColorTableMode.Local;
clone.DecodedGlobalColorTable = new[] { Color.Black }; clone.GlobalColorTable = new[] { Color.Black };
Assert.False(meta.RepeatCount.Equals(clone.RepeatCount)); Assert.False(meta.RepeatCount.Equals(clone.RepeatCount));
Assert.False(meta.ColorTableMode.Equals(clone.ColorTableMode)); Assert.False(meta.ColorTableMode.Equals(clone.ColorTableMode));
Assert.False(meta.DecodedGlobalColorTable.Length == clone.DecodedGlobalColorTable.Length); Assert.False(meta.GlobalColorTable.Value.Length == clone.GlobalColorTable.Value.Length);
Assert.Equal(1, clone.DecodedGlobalColorTable.Length); Assert.Equal(1, clone.GlobalColorTable.Value.Length);
Assert.False(meta.Comments.Equals(clone.Comments)); Assert.False(meta.Comments.Equals(clone.Comments));
Assert.True(meta.Comments.SequenceEqual(clone.Comments)); Assert.True(meta.Comments.SequenceEqual(clone.Comments));
} }
@ -208,7 +208,7 @@ public class GifMetadataTests
if (colorTableMode == GifColorTableMode.Global) if (colorTableMode == GifColorTableMode.Global)
{ {
Assert.Equal(globalColorTableLength, gifMetadata.DecodedGlobalColorTable.Length); Assert.Equal(globalColorTableLength, gifMetadata.GlobalColorTable.Value.Length);
} }
Assert.Equal(frameDelay, gifFrameMetadata.FrameDelay); Assert.Equal(frameDelay, gifFrameMetadata.FrameDelay);

2
tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.NonGeneric.cs

@ -318,7 +318,7 @@ public abstract partial class ImageFrameCollectionTests
if (aData.ColorTableMode == GifColorTableMode.Local && bData.ColorTableMode == GifColorTableMode.Local) if (aData.ColorTableMode == GifColorTableMode.Local && bData.ColorTableMode == GifColorTableMode.Local)
{ {
Assert.Equal(aData.DecodedLocalColorTable.Length, bData.DecodedLocalColorTable.Length); Assert.Equal(aData.LocalColorTable.Value.Length, bData.LocalColorTable.Value.Length);
} }
} }
} }

4
tests/ImageSharp.Tests/Metadata/ImageFrameMetadataTests.cs

@ -26,14 +26,14 @@ public class ImageFrameMetadataTests
ImageFrameMetadata metaData = new(); ImageFrameMetadata metaData = new();
GifFrameMetadata gifFrameMetadata = metaData.GetGifMetadata(); GifFrameMetadata gifFrameMetadata = metaData.GetGifMetadata();
gifFrameMetadata.FrameDelay = frameDelay; gifFrameMetadata.FrameDelay = frameDelay;
gifFrameMetadata.DecodedLocalColorTable = Enumerable.Repeat(Color.HotPink, colorTableLength).ToArray(); gifFrameMetadata.LocalColorTable = Enumerable.Repeat(Color.HotPink, colorTableLength).ToArray();
gifFrameMetadata.DisposalMethod = disposalMethod; gifFrameMetadata.DisposalMethod = disposalMethod;
ImageFrameMetadata clone = new(metaData); ImageFrameMetadata clone = new(metaData);
GifFrameMetadata cloneGifFrameMetadata = clone.GetGifMetadata(); GifFrameMetadata cloneGifFrameMetadata = clone.GetGifMetadata();
Assert.Equal(frameDelay, cloneGifFrameMetadata.FrameDelay); Assert.Equal(frameDelay, cloneGifFrameMetadata.FrameDelay);
Assert.Equal(colorTableLength, cloneGifFrameMetadata.DecodedLocalColorTable.Length); Assert.Equal(colorTableLength, cloneGifFrameMetadata.LocalColorTable.Value.Length);
Assert.Equal(disposalMethod, cloneGifFrameMetadata.DisposalMethod); Assert.Equal(disposalMethod, cloneGifFrameMetadata.DisposalMethod);
} }

2
tests/ImageSharp.Tests/TestImages.cs

@ -482,6 +482,8 @@ public static class TestImages
public const string Issue2288_B = "Gif/issues/issue_2288_2.gif"; public const string Issue2288_B = "Gif/issues/issue_2288_2.gif";
public const string Issue2288_C = "Gif/issues/issue_2288_3.gif"; public const string Issue2288_C = "Gif/issues/issue_2288_3.gif";
public const string Issue2288_D = "Gif/issues/issue_2288_4.gif"; public const string Issue2288_D = "Gif/issues/issue_2288_4.gif";
public const string Issue2450 = "Gif/issues/issue_2450.gif";
public const string Issue2198 = "Gif/issues/issue_2198.gif";
} }
public static readonly string[] All = { Rings, Giphy, Cheers, Trans, Kumin, Leo, Ratio4x1, Ratio1x4 }; public static readonly string[] All = { Rings, Giphy, Cheers, Trans, Kumin, Leo, Ratio4x1, Ratio1x4 };

Loading…
Cancel
Save