Skip to content

Commit 502a354

Browse files
Add tests and fix issues.
1 parent 8dd4f35 commit 502a354

26 files changed

+784
-208
lines changed

src/ImageSharp/Formats/Cur/CurFrameMetadata.cs

Lines changed: 21 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,13 @@ private CurFrameMetadata(CurFrameMetadata other)
4848
/// Gets or sets the encoding width. <br />
4949
/// Can be any number between 0 and 255. Value 0 means a frame height of 256 pixels or greater.
5050
/// </summary>
51-
public byte EncodingWidth { get; set; }
51+
public byte? EncodingWidth { get; set; }
5252

5353
/// <summary>
5454
/// Gets or sets the encoding height. <br />
5555
/// Can be any number between 0 and 255. Value 0 means a frame height of 256 pixels or greater.
5656
/// </summary>
57-
public byte EncodingHeight { get; set; }
57+
public byte? EncodingHeight { get; set; }
5858

5959
/// <summary>
6060
/// Gets or sets the number of bits per pixel.<br/>
@@ -80,20 +80,6 @@ public static CurFrameMetadata FromFormatConnectingFrameMetadata(FormatConnectin
8080
};
8181
}
8282

83-
byte encodingWidth = metadata.EncodingWidth switch
84-
{
85-
> 255 => 0,
86-
<= 255 and >= 1 => (byte)metadata.EncodingWidth,
87-
_ => 0
88-
};
89-
90-
byte encodingHeight = metadata.EncodingHeight switch
91-
{
92-
> 255 => 0,
93-
<= 255 and >= 1 => (byte)metadata.EncodingHeight,
94-
_ => 0
95-
};
96-
9783
int bpp = metadata.PixelTypeInfo.Value.BitsPerPixel;
9884
BmpBitsPerPixel bbpp = bpp switch
9985
{
@@ -116,8 +102,8 @@ public static CurFrameMetadata FromFormatConnectingFrameMetadata(FormatConnectin
116102
{
117103
BmpBitsPerPixel = bbpp,
118104
Compression = compression,
119-
EncodingWidth = encodingWidth,
120-
EncodingHeight = encodingHeight,
105+
EncodingWidth = ClampEncodingDimension(metadata.EncodingWidth),
106+
EncodingHeight = ClampEncodingDimension(metadata.EncodingHeight),
121107
ColorTable = compression == IconFrameCompression.Bmp ? metadata.ColorTable : null
122108
};
123109
}
@@ -138,8 +124,8 @@ public void AfterFrameApply<TPixel>(ImageFrame<TPixel> source, ImageFrame<TPixel
138124
{
139125
float ratioX = destination.Width / (float)source.Width;
140126
float ratioY = destination.Height / (float)source.Height;
141-
this.EncodingWidth = Scale(this.EncodingWidth, destination.Width, ratioX);
142-
this.EncodingHeight = Scale(this.EncodingHeight, destination.Height, ratioY);
127+
this.EncodingWidth = ScaleEncodingDimension(this.EncodingWidth, destination.Width, ratioX);
128+
this.EncodingHeight = ScaleEncodingDimension(this.EncodingHeight, destination.Height, ratioY);
143129
}
144130

145131
/// <inheritdoc/>
@@ -156,16 +142,16 @@ internal void FromIconDirEntry(IconDirEntry entry)
156142
this.HotspotY = entry.BitCount;
157143
}
158144

159-
internal IconDirEntry ToIconDirEntry()
145+
internal IconDirEntry ToIconDirEntry(Size size)
160146
{
161147
byte colorCount = this.Compression == IconFrameCompression.Png || this.BmpBitsPerPixel > BmpBitsPerPixel.Bit8
162148
? (byte)0
163149
: (byte)ColorNumerics.GetColorCountForBitDepth((int)this.BmpBitsPerPixel);
164150

165151
return new()
166152
{
167-
Width = this.EncodingWidth,
168-
Height = this.EncodingHeight,
153+
Width = ClampEncodingDimension(this.EncodingWidth ?? size.Width),
154+
Height = ClampEncodingDimension(this.EncodingHeight ?? size.Height),
169155
Planes = this.HotspotX,
170156
BitCount = this.HotspotY,
171157
ColorCount = colorCount
@@ -233,13 +219,22 @@ private PixelTypeInfo GetPixelTypeInfo()
233219
};
234220
}
235221

236-
private static byte Scale(byte? value, int destination, float ratio)
222+
private static byte ScaleEncodingDimension(byte? value, int destination, float ratio)
237223
{
238224
if (value is null)
239225
{
240-
return (byte)Math.Clamp(destination, 0, 255);
226+
return ClampEncodingDimension(destination);
241227
}
242228

243-
return Math.Min((byte)MathF.Ceiling(value.Value * ratio), (byte)Math.Clamp(destination, 0, 255));
229+
return ClampEncodingDimension(MathF.Ceiling(value.Value * ratio));
244230
}
231+
232+
private static byte ClampEncodingDimension(float? dimension)
233+
=> dimension switch
234+
{
235+
// Encoding dimensions can be between 0-256 where 0 means 256 or greater.
236+
> 255 => 0,
237+
<= 255 and >= 1 => (byte)dimension,
238+
_ => 0
239+
};
245240
}

src/ImageSharp/Formats/Gif/GifEncoderCore.cs

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -207,22 +207,29 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken
207207
this.WriteApplicationExtensions(stream, image.Frames.Count, this.repeatCount ?? gifMetadata.RepeatCount, xmpProfile);
208208
}
209209

210-
this.EncodeFirstFrame(stream, frameMetadata, quantized);
211-
212-
// Capture the global palette for reuse on subsequent frames and cleanup the quantized frame.
213-
TPixel[] globalPalette = image.Frames.Count == 1 ? [] : quantized.Palette.ToArray();
214-
215-
this.EncodeAdditionalFrames(
216-
stream,
217-
image,
218-
globalPalette,
219-
derivedTransparencyIndex,
220-
frameMetadata.DisposalMode,
221-
cancellationToken);
222-
223-
stream.WriteByte(GifConstants.EndIntroducer);
210+
// If the token is cancelled during encoding of frames we must ensure the
211+
// quantized frame is disposed.
212+
try
213+
{
214+
this.EncodeFirstFrame(stream, frameMetadata, quantized, cancellationToken);
215+
216+
// Capture the global palette for reuse on subsequent frames and cleanup the quantized frame.
217+
TPixel[] globalPalette = image.Frames.Count == 1 ? [] : quantized.Palette.ToArray();
218+
219+
this.EncodeAdditionalFrames(
220+
stream,
221+
image,
222+
globalPalette,
223+
derivedTransparencyIndex,
224+
frameMetadata.DisposalMode,
225+
cancellationToken);
226+
}
227+
finally
228+
{
229+
stream.WriteByte(GifConstants.EndIntroducer);
224230

225-
quantized?.Dispose();
231+
quantized?.Dispose();
232+
}
226233
}
227234

228235
private static GifFrameMetadata GetGifFrameMetadata<TPixel>(ImageFrame<TPixel> frame, int transparencyIndex)
@@ -310,9 +317,12 @@ private void EncodeAdditionalFrames<TPixel>(
310317
private void EncodeFirstFrame<TPixel>(
311318
Stream stream,
312319
GifFrameMetadata metadata,
313-
IndexedImageFrame<TPixel> quantized)
320+
IndexedImageFrame<TPixel> quantized,
321+
CancellationToken cancellationToken)
314322
where TPixel : unmanaged, IPixel<TPixel>
315323
{
324+
cancellationToken.ThrowIfCancellationRequested();
325+
316326
this.WriteGraphicalControlExtension(metadata, stream);
317327

318328
Buffer2D<byte> indices = ((IPixelSource)quantized).PixelBuffer;

src/ImageSharp/Formats/Ico/IcoFrameMetadata.cs

Lines changed: 21 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,13 @@ private IcoFrameMetadata(IcoFrameMetadata other)
4141
/// Gets or sets the encoding width. <br />
4242
/// Can be any number between 0 and 255. Value 0 means a frame height of 256 pixels or greater.
4343
/// </summary>
44-
public byte EncodingWidth { get; set; }
44+
public byte? EncodingWidth { get; set; }
4545

4646
/// <summary>
4747
/// Gets or sets the encoding height. <br />
4848
/// Can be any number between 0 and 255. Value 0 means a frame height of 256 pixels or greater.
4949
/// </summary>
50-
public byte EncodingHeight { get; set; }
50+
public byte? EncodingHeight { get; set; }
5151

5252
/// <summary>
5353
/// Gets or sets the number of bits per pixel.<br/>
@@ -73,20 +73,6 @@ public static IcoFrameMetadata FromFormatConnectingFrameMetadata(FormatConnectin
7373
};
7474
}
7575

76-
byte encodingWidth = metadata.EncodingWidth switch
77-
{
78-
> 255 => 0,
79-
<= 255 and >= 1 => (byte)metadata.EncodingWidth,
80-
_ => 0
81-
};
82-
83-
byte encodingHeight = metadata.EncodingHeight switch
84-
{
85-
> 255 => 0,
86-
<= 255 and >= 1 => (byte)metadata.EncodingHeight,
87-
_ => 0
88-
};
89-
9076
int bpp = metadata.PixelTypeInfo.Value.BitsPerPixel;
9177
BmpBitsPerPixel bbpp = bpp switch
9278
{
@@ -109,8 +95,8 @@ public static IcoFrameMetadata FromFormatConnectingFrameMetadata(FormatConnectin
10995
{
11096
BmpBitsPerPixel = bbpp,
11197
Compression = compression,
112-
EncodingWidth = encodingWidth,
113-
EncodingHeight = encodingHeight,
98+
EncodingWidth = ClampEncodingDimension(metadata.EncodingWidth),
99+
EncodingHeight = ClampEncodingDimension(metadata.EncodingHeight),
114100
ColorTable = compression == IconFrameCompression.Bmp ? metadata.ColorTable : null
115101
};
116102
}
@@ -131,8 +117,8 @@ public void AfterFrameApply<TPixel>(ImageFrame<TPixel> source, ImageFrame<TPixel
131117
{
132118
float ratioX = destination.Width / (float)source.Width;
133119
float ratioY = destination.Height / (float)source.Height;
134-
this.EncodingWidth = Scale(this.EncodingWidth, destination.Width, ratioX);
135-
this.EncodingHeight = Scale(this.EncodingHeight, destination.Height, ratioY);
120+
this.EncodingWidth = ScaleEncodingDimension(this.EncodingWidth, destination.Width, ratioX);
121+
this.EncodingHeight = ScaleEncodingDimension(this.EncodingHeight, destination.Height, ratioY);
136122
}
137123

138124
/// <inheritdoc/>
@@ -147,16 +133,16 @@ internal void FromIconDirEntry(IconDirEntry entry)
147133
this.EncodingHeight = entry.Height;
148134
}
149135

150-
internal IconDirEntry ToIconDirEntry()
136+
internal IconDirEntry ToIconDirEntry(Size size)
151137
{
152138
byte colorCount = this.Compression == IconFrameCompression.Png || this.BmpBitsPerPixel > BmpBitsPerPixel.Bit8
153139
? (byte)0
154140
: (byte)ColorNumerics.GetColorCountForBitDepth((int)this.BmpBitsPerPixel);
155141

156142
return new()
157143
{
158-
Width = this.EncodingWidth,
159-
Height = this.EncodingHeight,
144+
Width = ClampEncodingDimension(this.EncodingWidth ?? size.Width),
145+
Height = ClampEncodingDimension(this.EncodingHeight ?? size.Height),
160146
Planes = 1,
161147
ColorCount = colorCount,
162148
BitCount = this.Compression switch
@@ -228,13 +214,22 @@ private PixelTypeInfo GetPixelTypeInfo()
228214
};
229215
}
230216

231-
private static byte Scale(byte? value, int destination, float ratio)
217+
private static byte ScaleEncodingDimension(byte? value, int destination, float ratio)
232218
{
233219
if (value is null)
234220
{
235-
return (byte)Math.Clamp(destination, 0, 255);
221+
return ClampEncodingDimension(destination);
236222
}
237223

238-
return Math.Min((byte)MathF.Ceiling(value.Value * ratio), (byte)Math.Clamp(destination, 0, 255));
224+
return ClampEncodingDimension(MathF.Ceiling(value.Value * ratio));
239225
}
226+
227+
private static byte ClampEncodingDimension(float? dimension)
228+
=> dimension switch
229+
{
230+
// Encoding dimensions can be between 0-256 where 0 means 256 or greater.
231+
> 255 => 0,
232+
<= 255 and >= 1 => (byte)dimension,
233+
_ => 0
234+
};
240235
}

src/ImageSharp/Formats/Icon/IconEncoderCore.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,13 +123,13 @@ private void InitHeader(Image image)
123123
image.Frames.Select(i =>
124124
{
125125
IcoFrameMetadata metadata = i.Metadata.GetIcoMetadata();
126-
return new EncodingFrameMetadata(metadata.Compression, metadata.BmpBitsPerPixel, metadata.ColorTable, metadata.ToIconDirEntry());
126+
return new EncodingFrameMetadata(metadata.Compression, metadata.BmpBitsPerPixel, metadata.ColorTable, metadata.ToIconDirEntry(i.Size));
127127
}).ToArray(),
128128
IconFileType.CUR =>
129129
image.Frames.Select(i =>
130130
{
131131
CurFrameMetadata metadata = i.Metadata.GetCurMetadata();
132-
return new EncodingFrameMetadata(metadata.Compression, metadata.BmpBitsPerPixel, metadata.ColorTable, metadata.ToIconDirEntry());
132+
return new EncodingFrameMetadata(metadata.Compression, metadata.BmpBitsPerPixel, metadata.ColorTable, metadata.ToIconDirEntry(i.Size));
133133
}).ToArray(),
134134
_ => throw new NotSupportedException(),
135135
};

0 commit comments

Comments
 (0)