Skip to content

Commit 8166213

Browse files
Implement GifFrameMetadata
1 parent 2752a45 commit 8166213

15 files changed

Lines changed: 101 additions & 95 deletions

src/ImageSharp/Formats/Bmp/BmpMetadata.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,8 @@ public FormatConnectingFrameMetadata ToFormatConnectingFrameMetadata()
138138
=> new();
139139

140140
/// <inheritdoc/>
141-
public IDeepCloneable DeepClone() => ((IDeepCloneable<BmpMetadata>)this).DeepClone();
141+
IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone();
142142

143143
/// <inheritdoc/>
144-
BmpMetadata IDeepCloneable<BmpMetadata>.DeepClone() => new(this);
144+
public BmpMetadata DeepClone() => new(this);
145145
}

src/ImageSharp/Formats/Gif/GifDecoderCore.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,7 @@ private void ReadFrameColors<TPixel>(ref Image<TPixel>? image, ref ImageFrame<TP
517517
}
518518
else
519519
{
520-
if (this.graphicsControlExtension.DisposalMethod == GifDisposalMethod.RestoreToPrevious)
520+
if (this.graphicsControlExtension.DisposalMethod == FrameDisposalMode.RestoreToPrevious)
521521
{
522522
prevFrame = previousFrame;
523523
}
@@ -624,7 +624,7 @@ private void ReadFrameColors<TPixel>(ref Image<TPixel>? image, ref ImageFrame<TP
624624

625625
previousFrame = currentFrame ?? image.Frames.RootFrame;
626626

627-
if (this.graphicsControlExtension.DisposalMethod == GifDisposalMethod.RestoreToBackground)
627+
if (this.graphicsControlExtension.DisposalMethod == FrameDisposalMode.RestoreToBackground)
628628
{
629629
this.restoreArea = new Rectangle(descriptor.Left, descriptor.Top, descriptor.Width, descriptor.Height);
630630
}

src/ImageSharp/Formats/Gif/GifDisposalMethod.cs

Lines changed: 0 additions & 37 deletions
This file was deleted.

src/ImageSharp/Formats/Gif/GifEncoderCore.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ private void EncodeAdditionalFrames<TPixel>(
235235
Image<TPixel> image,
236236
ReadOnlyMemory<TPixel> globalPalette,
237237
int globalTransparencyIndex,
238-
GifDisposalMethod previousDisposalMethod)
238+
FrameDisposalMode previousDisposalMode)
239239
where TPixel : unmanaged, IPixel<TPixel>
240240
{
241241
if (image.Frames.Count == 1)
@@ -279,10 +279,10 @@ private void EncodeAdditionalFrames<TPixel>(
279279
useLocal,
280280
gifMetadata,
281281
paletteQuantizer,
282-
previousDisposalMethod);
282+
previousDisposalMode);
283283

284284
previousFrame = currentFrame;
285-
previousDisposalMethod = gifMetadata.DisposalMethod;
285+
previousDisposalMode = gifMetadata.DisposalMethod;
286286
}
287287

288288
if (hasPaletteQuantizer)
@@ -323,14 +323,14 @@ private void EncodeAdditionalFrame<TPixel>(
323323
bool useLocal,
324324
GifFrameMetadata metadata,
325325
PaletteQuantizer<TPixel> globalPaletteQuantizer,
326-
GifDisposalMethod previousDisposal)
326+
FrameDisposalMode previousDisposalMode)
327327
where TPixel : unmanaged, IPixel<TPixel>
328328
{
329329
// Capture any explicit transparency index from the metadata.
330330
// We use it to determine the value to use to replace duplicate pixels.
331331
int transparencyIndex = metadata.HasTransparency ? metadata.TransparencyIndex : -1;
332332

333-
ImageFrame<TPixel>? previous = previousDisposal == GifDisposalMethod.RestoreToBackground ? null : previousFrame;
333+
ImageFrame<TPixel>? previous = previousDisposalMode == FrameDisposalMode.RestoreToBackground ? null : previousFrame;
334334

335335
// Deduplicate and quantize the frame capturing only required parts.
336336
(bool difference, Rectangle bounds) =
@@ -664,7 +664,7 @@ private void WriteGraphicalControlExtension(GifFrameMetadata metadata, Stream st
664664
bool hasTransparency = metadata.HasTransparency;
665665

666666
byte packedValue = GifGraphicControlExtension.GetPackedValue(
667-
disposalMethod: metadata.DisposalMethod,
667+
disposalMode: metadata.DisposalMethod,
668668
transparencyFlag: hasTransparency);
669669

670670
GifGraphicControlExtension extension = new(

src/ImageSharp/Formats/Gif/GifFrameMetadata.cs

Lines changed: 59 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Gif;
99
/// <summary>
1010
/// Provides Gif specific metadata information for the image frame.
1111
/// </summary>
12-
public class GifFrameMetadata : IDeepCloneable
12+
public class GifFrameMetadata : IFormatFrameMetadata<GifFrameMetadata>
1313
{
1414
/// <summary>
1515
/// Initializes a new instance of the <see cref="GifFrameMetadata"/> class.
@@ -73,10 +73,65 @@ private GifFrameMetadata(GifFrameMetadata other)
7373
/// Primarily used in Gif animation, this field indicates the way in which the graphic is to
7474
/// be treated after being displayed.
7575
/// </summary>
76-
public GifDisposalMethod DisposalMethod { get; set; }
76+
public FrameDisposalMode DisposalMethod { get; set; }
77+
78+
/// <inheritdoc />
79+
public static GifFrameMetadata FromFormatConnectingFrameMetadata(FormatConnectingFrameMetadata metadata)
80+
{
81+
int index = -1;
82+
const float background = 1f;
83+
if (metadata.ColorTable.HasValue)
84+
{
85+
ReadOnlySpan<Color> colorTable = metadata.ColorTable.Value.Span;
86+
for (int i = 0; i < colorTable.Length; i++)
87+
{
88+
Vector4 vector = colorTable[i].ToScaledVector4();
89+
if (vector.W < background)
90+
{
91+
index = i;
92+
}
93+
}
94+
}
95+
96+
bool hasTransparency = index >= 0;
97+
98+
return new()
99+
{
100+
LocalColorTable = metadata.ColorTable,
101+
ColorTableMode = metadata.ColorTableMode,
102+
FrameDelay = (int)Math.Round(metadata.Duration.TotalMilliseconds / 10),
103+
DisposalMethod = metadata.DisposalMode,
104+
HasTransparency = hasTransparency,
105+
TransparencyIndex = hasTransparency ? unchecked((byte)index) : byte.MinValue,
106+
};
107+
}
108+
109+
/// <inheritdoc />
110+
public FormatConnectingFrameMetadata ToFormatConnectingFrameMetadata()
111+
{
112+
// throw new NotImplementedException();
113+
// For most scenarios we would consider the blend method to be 'Over' however if a frame has a disposal method of 'RestoreToBackground' or
114+
// has a local palette with 256 colors and is not transparent we should use 'Source'.
115+
bool blendSource = this.DisposalMethod == FrameDisposalMode.RestoreToBackground || (this.LocalColorTable?.Length == 256 && !this.HasTransparency);
116+
117+
// If the color table is global and frame has no transparency. Consider it 'Source' also.
118+
blendSource |= this.ColorTableMode == FrameColorTableMode.Global && !this.HasTransparency;
119+
120+
return new()
121+
{
122+
ColorTable = this.LocalColorTable,
123+
ColorTableMode = this.ColorTableMode,
124+
Duration = TimeSpan.FromMilliseconds(this.FrameDelay * 10),
125+
DisposalMode = this.DisposalMethod,
126+
BlendMode = blendSource ? FrameBlendMode.Source : FrameBlendMode.Over,
127+
};
128+
}
129+
130+
/// <inheritdoc/>
131+
IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone();
77132

78133
/// <inheritdoc/>
79-
public IDeepCloneable DeepClone() => new GifFrameMetadata(this);
134+
public GifFrameMetadata DeepClone() => new(this);
80135

81136
internal static GifFrameMetadata FromAnimatedMetadata(AnimatedImageFrameMetadata metadata)
82137
{
@@ -103,17 +158,9 @@ internal static GifFrameMetadata FromAnimatedMetadata(AnimatedImageFrameMetadata
103158
LocalColorTable = metadata.ColorTable,
104159
ColorTableMode = metadata.ColorTableMode,
105160
FrameDelay = (int)Math.Round(metadata.Duration.TotalMilliseconds / 10),
106-
DisposalMethod = GetMode(metadata.DisposalMode),
161+
DisposalMethod = metadata.DisposalMode,
107162
HasTransparency = hasTransparency,
108163
TransparencyIndex = hasTransparency ? unchecked((byte)index) : byte.MinValue,
109164
};
110165
}
111-
112-
private static GifDisposalMethod GetMode(FrameDisposalMode mode) => mode switch
113-
{
114-
FrameDisposalMode.DoNotDispose => GifDisposalMethod.NotDispose,
115-
FrameDisposalMode.RestoreToBackground => GifDisposalMethod.RestoreToBackground,
116-
FrameDisposalMode.RestoreToPrevious => GifDisposalMethod.RestoreToPrevious,
117-
_ => GifDisposalMethod.Unspecified,
118-
};
119166
}

src/ImageSharp/Formats/Gif/GifMetadata.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,8 @@ public FormatConnectingMetadata ToFormatConnectingMetadata()
145145
}
146146

147147
/// <inheritdoc/>
148-
public IDeepCloneable DeepClone() => ((IDeepCloneable<GifMetadata>)this).DeepClone();
148+
IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone();
149149

150150
/// <inheritdoc/>
151-
GifMetadata IDeepCloneable<GifMetadata>.DeepClone() => new(this);
151+
public GifMetadata DeepClone() => new(this);
152152
}

src/ImageSharp/Formats/Gif/MetadataExtensions.cs

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ internal static AnimatedImageFrameMetadata ToAnimatedImageFrameMetadata(this Gif
8080
{
8181
// For most scenarios we would consider the blend method to be 'Over' however if a frame has a disposal method of 'RestoreToBackground' or
8282
// 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);
83+
bool blendSource = source.DisposalMethod == FrameDisposalMode.RestoreToBackground || (source.LocalColorTable?.Length == 256 && !source.HasTransparency);
8484

8585
// If the color table is global and frame has no transparency. Consider it 'Source' also.
8686
blendSource |= source.ColorTableMode == FrameColorTableMode.Global && !source.HasTransparency;
@@ -90,16 +90,8 @@ internal static AnimatedImageFrameMetadata ToAnimatedImageFrameMetadata(this Gif
9090
ColorTable = source.LocalColorTable,
9191
ColorTableMode = source.ColorTableMode,
9292
Duration = TimeSpan.FromMilliseconds(source.FrameDelay * 10),
93-
DisposalMode = GetMode(source.DisposalMethod),
93+
DisposalMode = source.DisposalMethod,
9494
BlendMode = blendSource ? FrameBlendMode.Source : FrameBlendMode.Over,
9595
};
9696
}
97-
98-
private static FrameDisposalMode GetMode(GifDisposalMethod method) => method switch
99-
{
100-
GifDisposalMethod.NotDispose => FrameDisposalMode.DoNotDispose,
101-
GifDisposalMethod.RestoreToBackground => FrameDisposalMode.RestoreToBackground,
102-
GifDisposalMethod.RestoreToPrevious => FrameDisposalMode.RestoreToPrevious,
103-
_ => FrameDisposalMode.Unspecified,
104-
};
10597
}

src/ImageSharp/Formats/Gif/Sections/GifGraphicControlExtension.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public GifGraphicControlExtension(
5252
/// Gets the disposal method which indicates the way in which the
5353
/// graphic is to be treated after being displayed.
5454
/// </summary>
55-
public GifDisposalMethod DisposalMethod => (GifDisposalMethod)((this.Packed & 0x1C) >> 2);
55+
public FrameDisposalMode DisposalMethod => (FrameDisposalMode)((this.Packed & 0x1C) >> 2);
5656

5757
/// <summary>
5858
/// Gets a value indicating whether transparency flag is to be set.
@@ -80,7 +80,7 @@ public int WriteTo(Span<byte> buffer)
8080
public static GifGraphicControlExtension Parse(ReadOnlySpan<byte> buffer)
8181
=> MemoryMarshal.Cast<byte, GifGraphicControlExtension>(buffer)[0];
8282

83-
public static byte GetPackedValue(GifDisposalMethod disposalMethod, bool userInputFlag = false, bool transparencyFlag = false)
83+
public static byte GetPackedValue(FrameDisposalMode disposalMode, bool userInputFlag = false, bool transparencyFlag = false)
8484
{
8585
/*
8686
Reserved | 3 Bits
@@ -91,7 +91,7 @@ Transparent Color Flag | 1 Bit
9191

9292
byte value = 0;
9393

94-
value |= (byte)((int)disposalMethod << 2);
94+
value |= (byte)((int)disposalMode << 2);
9595

9696
if (userInputFlag)
9797
{

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -309,11 +309,11 @@ public void Encode_AnimatedFormatTransform_FromPng<TPixel>(TestImageProvider<TPi
309309
switch (pngF.DisposalMethod)
310310
{
311311
case PngDisposalMethod.RestoreToBackground:
312-
Assert.Equal(GifDisposalMethod.RestoreToBackground, gifF.DisposalMethod);
312+
Assert.Equal(FrameDisposalMode.RestoreToBackground, gifF.DisposalMethod);
313313
break;
314314
case PngDisposalMethod.DoNotDispose:
315315
default:
316-
Assert.Equal(GifDisposalMethod.NotDispose, gifF.DisposalMethod);
316+
Assert.Equal(FrameDisposalMode.DoNotDispose, gifF.DisposalMethod);
317317
break;
318318
}
319319
}
@@ -359,11 +359,11 @@ public void Encode_AnimatedFormatTransform_FromWebp<TPixel>(TestImageProvider<TP
359359
switch (webpF.DisposalMethod)
360360
{
361361
case WebpDisposalMethod.RestoreToBackground:
362-
Assert.Equal(GifDisposalMethod.RestoreToBackground, gifF.DisposalMethod);
362+
Assert.Equal(FrameDisposalMode.RestoreToBackground, gifF.DisposalMethod);
363363
break;
364364
case WebpDisposalMethod.DoNotDispose:
365365
default:
366-
Assert.Equal(GifDisposalMethod.NotDispose, gifF.DisposalMethod);
366+
Assert.Equal(FrameDisposalMode.DoNotDispose, gifF.DisposalMethod);
367367
break;
368368
}
369369
}

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) Six Labors.
22
// Licensed under the Six Labors Split License.
33

4+
using SixLabors.ImageSharp.Formats;
45
using SixLabors.ImageSharp.Formats.Gif;
56

67
namespace SixLabors.ImageSharp.Tests.Formats.Gif;
@@ -14,14 +15,14 @@ public void CloneIsDeep()
1415
GifFrameMetadata meta = new()
1516
{
1617
FrameDelay = 1,
17-
DisposalMethod = GifDisposalMethod.RestoreToBackground,
18+
DisposalMethod = FrameDisposalMode.RestoreToBackground,
1819
LocalColorTable = new[] { Color.Black, Color.White }
1920
};
2021

2122
GifFrameMetadata clone = (GifFrameMetadata)meta.DeepClone();
2223

2324
clone.FrameDelay = 2;
24-
clone.DisposalMethod = GifDisposalMethod.RestoreToPrevious;
25+
clone.DisposalMethod = FrameDisposalMode.RestoreToPrevious;
2526
clone.LocalColorTable = new[] { Color.Black };
2627

2728
Assert.False(meta.FrameDelay.Equals(clone.FrameDelay));

0 commit comments

Comments
 (0)