@@ -45,14 +45,6 @@ internal sealed class GifEncoderCore
4545 /// </summary>
4646 private readonly IPixelSamplingStrategy pixelSamplingStrategy ;
4747
48- /// <summary>
49- /// The default background color of the canvas when animating.
50- /// This color may be used to fill the unused space on the canvas around the frames,
51- /// as well as the transparent pixels of the first frame.
52- /// The background color is also used when a frame disposal mode is <see cref="FrameDisposalMode.RestoreToBackground"/>.
53- /// </summary>
54- private readonly Color ? backgroundColor ;
55-
5648 /// <summary>
5749 /// The number of times any animation is repeated.
5850 /// </summary>
@@ -76,7 +68,6 @@ public GifEncoderCore(Configuration configuration, GifEncoder encoder)
7668 this . skipMetadata = encoder . SkipMetadata ;
7769 this . colorTableMode = encoder . ColorTableMode ;
7870 this . pixelSamplingStrategy = encoder . PixelSamplingStrategy ;
79- this . backgroundColor = encoder . BackgroundColor ;
8071 this . repeatCount = encoder . RepeatCount ;
8172 this . transparentColorMode = encoder . TransparentColorMode ;
8273 }
@@ -113,7 +104,17 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken
113104 TransparentColorMode mode = this . transparentColorMode ;
114105
115106 // Create a new quantizer options instance augmenting the transparent color mode to match the encoder.
116- QuantizerOptions options = ( this . encoder . Quantizer ? . Options ?? new QuantizerOptions ( ) ) . DeepClone ( o => o . TransparentColorMode = mode ) ;
107+ QuantizerOptions options = ( this . encoder . Quantizer ? . Options ?? new QuantizerOptions ( ) ) . DeepClone ( o =>
108+ {
109+ o . TransparentColorMode = mode ;
110+
111+ // Animated GIF delta frames can use one padded color-table index as transparency.
112+ // Express that through MaxColors so custom quantizers receive the same budget.
113+ if ( image . Frames . Count > 1 && o . MaxColors == QuantizerConstants . MaxColors )
114+ {
115+ o . MaxColors = QuantizerConstants . MaxColors - 1 ;
116+ }
117+ } ) ;
117118
118119 if ( globalQuantizer is null )
119120 {
@@ -145,6 +146,11 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken
145146 IPixelSamplingStrategy strategy = this . pixelSamplingStrategy ;
146147
147148 ImageFrame < TPixel > encodingFrame = image . Frames . RootFrame ;
149+
150+ // This color is encoded as the logical-screen background index and is also
151+ // used when de-duplicating frames that restore to the GIF background.
152+ Color backgroundColor = this . encoder . BackgroundColor ?? gifMetadata . BackgroundColor ?? Color . Transparent ;
153+ byte backgroundIndex = 0 ;
148154 if ( useGlobalTableForFirstFrame )
149155 {
150156 using IQuantizer < TPixel > firstFrameQuantizer = globalQuantizer . CreatePixelSpecificQuantizer < TPixel > ( this . configuration , options ) ;
@@ -158,6 +164,8 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken
158164 }
159165
160166 quantized = firstFrameQuantizer . QuantizeFrame ( encodingFrame , encodingFrame . Bounds ) ;
167+ TPixel backgroundPixel = backgroundColor . ToPixel < TPixel > ( ) ;
168+ backgroundIndex = firstFrameQuantizer . GetQuantizedColor ( backgroundPixel , out _ ) ;
161169 }
162170 else
163171 {
@@ -184,8 +192,6 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken
184192 frameMetadata . TransparencyIndex = ClampIndex ( transparencyIndex ) ;
185193 }
186194
187- byte backgroundIndex = GetBackgroundIndex ( quantized , gifMetadata , this . backgroundColor ) ;
188-
189195 // Get the number of bits.
190196 int bitDepth = ColorNumerics . GetBitsNeededForColorDepth ( quantized . Palette . Length ) ;
191197 this . WriteLogicalScreenDescriptor ( image . Metadata , image . Width , image . Height , backgroundIndex , useGlobalTable , bitDepth , stream ) ;
@@ -222,6 +228,7 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken
222228 image ,
223229 globalQuantizer ,
224230 globalFrameQuantizer ,
231+ backgroundColor ,
225232 transparencyIndex ,
226233 frameMetadata . DisposalMode ,
227234 cancellationToken ) ;
@@ -253,6 +260,7 @@ private void EncodeAdditionalFrames<TPixel>(
253260 Image < TPixel > image ,
254261 IQuantizer globalQuantizer ,
255262 PaletteQuantizer < TPixel > globalFrameQuantizer ,
263+ Color backgroundColor ,
256264 int globalTransparencyIndex ,
257265 FrameDisposalMode previousDisposalMode ,
258266 CancellationToken cancellationToken )
@@ -284,6 +292,7 @@ private void EncodeAdditionalFrames<TPixel>(
284292 globalFrameQuantizer ,
285293 useLocal ,
286294 gifMetadata ,
295+ backgroundColor ,
287296 previousDisposalMode ) ;
288297
289298 previousFrame = currentFrame ;
@@ -327,6 +336,7 @@ private void EncodeAdditionalFrame<TPixel>(
327336 PaletteQuantizer < TPixel > globalFrameQuantizer ,
328337 bool useLocal ,
329338 GifFrameMetadata metadata ,
339+ Color backgroundColor ,
330340 FrameDisposalMode previousDisposalMode )
331341 where TPixel : unmanaged, IPixel < TPixel >
332342 {
@@ -347,7 +357,7 @@ private void EncodeAdditionalFrame<TPixel>(
347357 previous . Metadata . GetGifMetadata ( ) . DisposalMode ;
348358
349359 Color background = ! useTransparency && disposalMode == FrameDisposalMode . RestoreToBackground
350- ? this . backgroundColor ?? Color . Transparent
360+ ? backgroundColor
351361 : Color . Transparent ;
352362
353363 // Deduplicate and quantize the frame capturing only required parts.
@@ -534,44 +544,6 @@ private static int GetTransparentIndex<TPixel>(IndexedImageFrame<TPixel>? quanti
534544 return index ;
535545 }
536546
537- /// <summary>
538- /// Returns the index of the background color in the palette.
539- /// </summary>
540- /// <param name="quantized">The current quantized frame.</param>
541- /// <param name="metadata">The gif metadata</param>
542- /// <param name="background">The background color to match.</param>
543- /// <typeparam name="TPixel">The pixel format.</typeparam>
544- /// <returns>The <see cref="byte"/> index of the background color.</returns>
545- private static byte GetBackgroundIndex < TPixel > ( IndexedImageFrame < TPixel > ? quantized , GifMetadata metadata , Color ? background )
546- where TPixel : unmanaged, IPixel < TPixel >
547- {
548- int match = - 1 ;
549- if ( quantized != null )
550- {
551- if ( background . HasValue )
552- {
553- TPixel backgroundPixel = background . Value . ToPixel < TPixel > ( ) ;
554- ReadOnlySpan < TPixel > palette = quantized . Palette . Span ;
555- for ( int i = 0 ; i < palette . Length ; i ++ )
556- {
557- if ( ! backgroundPixel . Equals ( palette [ i ] ) )
558- {
559- continue ;
560- }
561-
562- match = i ;
563- break ;
564- }
565- }
566- else if ( metadata . BackgroundColorIndex < quantized . Palette . Length )
567- {
568- match = metadata . BackgroundColorIndex ;
569- }
570- }
571-
572- return ClampIndex ( match ) ;
573- }
574-
575547 /// <summary>
576548 /// Writes the file header signature and version to the stream.
577549 /// </summary>
0 commit comments