@@ -67,6 +67,8 @@ internal sealed class GifEncoderCore
67
67
/// </summary>
68
68
private readonly ushort ? repeatCount ;
69
69
70
+ private readonly TransparentColorMode transparentColorMode ;
71
+
70
72
/// <summary>
71
73
/// Initializes a new instance of the <see cref="GifEncoderCore"/> class.
72
74
/// </summary>
@@ -83,6 +85,7 @@ public GifEncoderCore(Configuration configuration, GifEncoder encoder)
83
85
this . pixelSamplingStrategy = encoder . PixelSamplingStrategy ;
84
86
this . backgroundColor = encoder . BackgroundColor ;
85
87
this . repeatCount = encoder . RepeatCount ;
88
+ this . transparentColorMode = encoder . TransparentColorMode ;
86
89
}
87
90
88
91
/// <summary>
@@ -131,18 +134,40 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken
131
134
}
132
135
}
133
136
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.
134
139
using ( IQuantizer < TPixel > frameQuantizer = this . quantizer . CreatePixelSpecificQuantizer < TPixel > ( this . configuration ) )
135
140
{
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
+
136
159
if ( useGlobalTable )
137
160
{
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 ) ;
140
163
}
141
164
else
142
165
{
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 ) ;
145
168
}
169
+
170
+ clonedFrame ? . Dispose ( ) ;
146
171
}
147
172
148
173
// Write the header.
@@ -236,52 +261,49 @@ private void EncodeAdditionalFrames<TPixel>(
236
261
// This frame is reused to store de-duplicated pixel buffers.
237
262
using ImageFrame < TPixel > encodingFrame = new ( previousFrame . Configuration , previousFrame . Size ) ;
238
263
239
- for ( int i = 1 ; i < image . Frames . Count ; i ++ )
264
+ try
240
265
{
241
- if ( cancellationToken . IsCancellationRequested )
266
+ for ( int i = 1 ; i < image . Frames . Count ; i ++ )
242
267
{
243
- if ( hasPaletteQuantizer )
244
- {
245
- paletteQuantizer . Dispose ( ) ;
246
- }
268
+ cancellationToken . ThrowIfCancellationRequested ( ) ;
247
269
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 ) ;
250
275
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
+ }
256
285
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 ;
265
299
}
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 ;
280
300
}
281
-
282
- if ( hasPaletteQuantizer )
301
+ finally
283
302
{
284
- paletteQuantizer . Dispose ( ) ;
303
+ if ( hasPaletteQuantizer )
304
+ {
305
+ paletteQuantizer . Dispose ( ) ;
306
+ }
285
307
}
286
308
}
287
309
@@ -324,7 +346,9 @@ private void EncodeAdditionalFrame<TPixel>(
324
346
// We use it to determine the value to use to replace duplicate pixels.
325
347
int transparencyIndex = metadata . HasTransparency ? metadata . TransparencyIndex : - 1 ;
326
348
327
- ImageFrame < TPixel > ? previous = previousDisposalMode == FrameDisposalMode . RestoreToBackground ? null : previousFrame ;
349
+ ImageFrame < TPixel > ? previous = previousDisposalMode == FrameDisposalMode . RestoreToBackground
350
+ ? null :
351
+ previousFrame ;
328
352
329
353
Color background = metadata . DisposalMode == FrameDisposalMode . RestoreToBackground
330
354
? this . backgroundColor ?? Color . Transparent
@@ -341,6 +365,11 @@ private void EncodeAdditionalFrame<TPixel>(
341
365
background ,
342
366
true ) ;
343
367
368
+ if ( EncodingUtilities . ShouldClearTransparentPixels < TPixel > ( this . transparentColorMode ) )
369
+ {
370
+ EncodingUtilities . ClearTransparentPixels ( encodingFrame , background ) ;
371
+ }
372
+
344
373
using IndexedImageFrame < TPixel > quantized = this . QuantizeAdditionalFrameAndUpdateMetadata (
345
374
encodingFrame ,
346
375
bounds ,
0 commit comments