@@ -67,6 +67,8 @@ internal sealed class GifEncoderCore
6767 /// </summary>
6868 private readonly ushort ? repeatCount ;
6969
70+ private readonly TransparentColorMode transparentColorMode ;
71+
7072 /// <summary>
7173 /// Initializes a new instance of the <see cref="GifEncoderCore"/> class.
7274 /// </summary>
@@ -83,6 +85,7 @@ public GifEncoderCore(Configuration configuration, GifEncoder encoder)
8385 this . pixelSamplingStrategy = encoder . PixelSamplingStrategy ;
8486 this . backgroundColor = encoder . BackgroundColor ;
8587 this . repeatCount = encoder . RepeatCount ;
88+ this . transparentColorMode = encoder . TransparentColorMode ;
8689 }
8790
8891 /// <summary>
@@ -131,18 +134,40 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken
131134 }
132135 }
133136
137+ // Quantize the first frame. Checking to see whether we can clear the transparent pixels
138+ // to allow for a smaller color palette and encoded result.
134139 using ( IQuantizer < TPixel > frameQuantizer = this . quantizer . CreatePixelSpecificQuantizer < TPixel > ( this . configuration ) )
135140 {
141+ ImageFrame < TPixel > ? clonedFrame = null ;
142+ Configuration configuration = this . configuration ;
143+ TransparentColorMode mode = this . transparentColorMode ;
144+ IPixelSamplingStrategy strategy = this . pixelSamplingStrategy ;
145+ if ( EncodingUtilities . ShouldClearTransparentPixels < TPixel > ( mode ) )
146+ {
147+ clonedFrame = image . Frames . RootFrame . Clone ( ) ;
148+
149+ GifFrameMetadata frameMeta = clonedFrame . Metadata . GetGifMetadata ( ) ;
150+ Color background = frameMeta . DisposalMode == FrameDisposalMode . RestoreToBackground
151+ ? this . backgroundColor ?? Color . Transparent
152+ : Color . Transparent ;
153+
154+ EncodingUtilities . ClearTransparentPixels ( clonedFrame , background ) ;
155+ }
156+
157+ ImageFrame < TPixel > encodingFrame = clonedFrame ?? image . Frames . RootFrame ;
158+
136159 if ( useGlobalTable )
137160 {
138- frameQuantizer . BuildPalette ( this . pixelSamplingStrategy , image ) ;
139- quantized = frameQuantizer . QuantizeFrame ( image . Frames . RootFrame , image . Bounds ) ;
161+ frameQuantizer . BuildPalette ( configuration , mode , strategy , image ) ;
162+ quantized = frameQuantizer . QuantizeFrame ( encodingFrame , image . Bounds ) ;
140163 }
141164 else
142165 {
143- frameQuantizer . BuildPalette ( this . pixelSamplingStrategy , image . Frames . RootFrame ) ;
144- quantized = frameQuantizer . QuantizeFrame ( image . Frames . RootFrame , image . Bounds ) ;
166+ frameQuantizer . BuildPalette ( configuration , mode , strategy , encodingFrame ) ;
167+ quantized = frameQuantizer . QuantizeFrame ( encodingFrame , image . Bounds ) ;
145168 }
169+
170+ clonedFrame ? . Dispose ( ) ;
146171 }
147172
148173 // Write the header.
@@ -236,52 +261,49 @@ private void EncodeAdditionalFrames<TPixel>(
236261 // This frame is reused to store de-duplicated pixel buffers.
237262 using ImageFrame < TPixel > encodingFrame = new ( previousFrame . Configuration , previousFrame . Size ) ;
238263
239- for ( int i = 1 ; i < image . Frames . Count ; i ++ )
264+ try
240265 {
241- if ( cancellationToken . IsCancellationRequested )
266+ for ( int i = 1 ; i < image . Frames . Count ; i ++ )
242267 {
243- if ( hasPaletteQuantizer )
244- {
245- paletteQuantizer . Dispose ( ) ;
246- }
268+ cancellationToken . ThrowIfCancellationRequested ( ) ;
247269
248- return ;
249- }
270+ // Gather the metadata for this frame.
271+ ImageFrame < TPixel > currentFrame = image . Frames [ i ] ;
272+ ImageFrame < TPixel > ? nextFrame = i < image . Frames . Count - 1 ? image . Frames [ i + 1 ] : null ;
273+ GifFrameMetadata gifMetadata = GetGifFrameMetadata ( currentFrame , globalTransparencyIndex ) ;
274+ bool useLocal = this . colorTableMode == FrameColorTableMode . Local || ( gifMetadata . ColorTableMode == FrameColorTableMode . Local ) ;
250275
251- // Gather the metadata for this frame.
252- ImageFrame < TPixel > currentFrame = image . Frames [ i ] ;
253- ImageFrame < TPixel > ? nextFrame = i < image . Frames . Count - 1 ? image . Frames [ i + 1 ] : null ;
254- GifFrameMetadata gifMetadata = GetGifFrameMetadata ( currentFrame , globalTransparencyIndex ) ;
255- bool useLocal = this . colorTableMode == FrameColorTableMode . Local || ( gifMetadata . ColorTableMode == FrameColorTableMode . Local ) ;
276+ if ( ! useLocal && ! hasPaletteQuantizer && i > 0 )
277+ {
278+ // The palette quantizer can reuse the same global pixel map across multiple frames since the palette is unchanging.
279+ // This allows a reduction of memory usage across multi-frame gifs using a global palette
280+ // and also allows use to reuse the cache from previous runs.
281+ int transparencyIndex = gifMetadata . HasTransparency ? gifMetadata . TransparencyIndex : - 1 ;
282+ paletteQuantizer = new ( this . configuration , this . quantizer ! . Options , globalPalette , transparencyIndex ) ;
283+ hasPaletteQuantizer = true ;
284+ }
256285
257- if ( ! useLocal && ! hasPaletteQuantizer && i > 0 )
258- {
259- // The palette quantizer can reuse the same global pixel map across multiple frames since the palette is unchanging.
260- // This allows a reduction of memory usage across multi-frame gifs using a global palette
261- // and also allows use to reuse the cache from previous runs.
262- int transparencyIndex = gifMetadata . HasTransparency ? gifMetadata . TransparencyIndex : - 1 ;
263- paletteQuantizer = new ( this . configuration , this . quantizer ! . Options , globalPalette , transparencyIndex ) ;
264- hasPaletteQuantizer = true ;
286+ this . EncodeAdditionalFrame (
287+ stream ,
288+ previousFrame ,
289+ currentFrame ,
290+ nextFrame ,
291+ encodingFrame ,
292+ useLocal ,
293+ gifMetadata ,
294+ paletteQuantizer ,
295+ previousDisposalMode ) ;
296+
297+ previousFrame = currentFrame ;
298+ previousDisposalMode = gifMetadata . DisposalMode ;
265299 }
266-
267- this . EncodeAdditionalFrame (
268- stream ,
269- previousFrame ,
270- currentFrame ,
271- nextFrame ,
272- encodingFrame ,
273- useLocal ,
274- gifMetadata ,
275- paletteQuantizer ,
276- previousDisposalMode ) ;
277-
278- previousFrame = currentFrame ;
279- previousDisposalMode = gifMetadata . DisposalMode ;
280300 }
281-
282- if ( hasPaletteQuantizer )
301+ finally
283302 {
284- paletteQuantizer . Dispose ( ) ;
303+ if ( hasPaletteQuantizer )
304+ {
305+ paletteQuantizer . Dispose ( ) ;
306+ }
285307 }
286308 }
287309
@@ -324,7 +346,9 @@ private void EncodeAdditionalFrame<TPixel>(
324346 // We use it to determine the value to use to replace duplicate pixels.
325347 int transparencyIndex = metadata . HasTransparency ? metadata . TransparencyIndex : - 1 ;
326348
327- ImageFrame < TPixel > ? previous = previousDisposalMode == FrameDisposalMode . RestoreToBackground ? null : previousFrame ;
349+ ImageFrame < TPixel > ? previous = previousDisposalMode == FrameDisposalMode . RestoreToBackground
350+ ? null :
351+ previousFrame ;
328352
329353 Color background = metadata . DisposalMode == FrameDisposalMode . RestoreToBackground
330354 ? this . backgroundColor ?? Color . Transparent
@@ -341,6 +365,11 @@ private void EncodeAdditionalFrame<TPixel>(
341365 background ,
342366 true ) ;
343367
368+ if ( EncodingUtilities . ShouldClearTransparentPixels < TPixel > ( this . transparentColorMode ) )
369+ {
370+ EncodingUtilities . ClearTransparentPixels ( encodingFrame , background ) ;
371+ }
372+
344373 using IndexedImageFrame < TPixel > quantized = this . QuantizeAdditionalFrameAndUpdateMetadata (
345374 encodingFrame ,
346375 bounds ,
0 commit comments