Skip to content

Commit 78fd1b9

Browse files
Merge branch 'main' into js/png-pallete
2 parents c7da070 + 945a83f commit 78fd1b9

18 files changed

Lines changed: 318 additions & 85 deletions

File tree

.github/workflows/build-and-test.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ on:
99
pull_request:
1010
branches:
1111
- main
12+
- release/*
1213
types: [ labeled, opened, synchronize, reopened ]
1314
jobs:
1415
Build:

src/ImageSharp/Advanced/ParallelRowIterator.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public static void IterateRows<T>(
5050
int width = rectangle.Width;
5151
int height = rectangle.Height;
5252

53-
int maxSteps = DivideCeil(width * height, parallelSettings.MinimumPixelsProcessedPerTask);
53+
int maxSteps = DivideCeil(width * (long)height, parallelSettings.MinimumPixelsProcessedPerTask);
5454
int numOfSteps = Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps);
5555

5656
// Avoid TPL overhead in this trivial case:
@@ -115,7 +115,7 @@ public static void IterateRows<T, TBuffer>(
115115
int width = rectangle.Width;
116116
int height = rectangle.Height;
117117

118-
int maxSteps = DivideCeil(width * height, parallelSettings.MinimumPixelsProcessedPerTask);
118+
int maxSteps = DivideCeil(width * (long)height, parallelSettings.MinimumPixelsProcessedPerTask);
119119
int numOfSteps = Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps);
120120
MemoryAllocator allocator = parallelSettings.MemoryAllocator;
121121
int bufferLength = Unsafe.AsRef(operation).GetRequiredBufferLength(rectangle);
@@ -180,7 +180,7 @@ public static void IterateRowIntervals<T>(
180180
int width = rectangle.Width;
181181
int height = rectangle.Height;
182182

183-
int maxSteps = DivideCeil(width * height, parallelSettings.MinimumPixelsProcessedPerTask);
183+
int maxSteps = DivideCeil(width * (long)height, parallelSettings.MinimumPixelsProcessedPerTask);
184184
int numOfSteps = Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps);
185185

186186
// Avoid TPL overhead in this trivial case:
@@ -242,7 +242,7 @@ public static void IterateRowIntervals<T, TBuffer>(
242242
int width = rectangle.Width;
243243
int height = rectangle.Height;
244244

245-
int maxSteps = DivideCeil(width * height, parallelSettings.MinimumPixelsProcessedPerTask);
245+
int maxSteps = DivideCeil(width * (long)height, parallelSettings.MinimumPixelsProcessedPerTask);
246246
int numOfSteps = Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps);
247247
MemoryAllocator allocator = parallelSettings.MemoryAllocator;
248248
int bufferLength = Unsafe.AsRef(operation).GetRequiredBufferLength(rectangle);
@@ -270,7 +270,7 @@ public static void IterateRowIntervals<T, TBuffer>(
270270
}
271271

272272
[MethodImpl(InliningOptions.ShortMethod)]
273-
private static int DivideCeil(int dividend, int divisor) => 1 + ((dividend - 1) / divisor);
273+
private static int DivideCeil(long dividend, int divisor) => (int)Math.Min(1 + ((dividend - 1) / divisor), int.MaxValue);
274274

275275
private static void ValidateRectangle(Rectangle rectangle)
276276
{

src/ImageSharp/Formats/Gif/GifDecoderCore.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -710,10 +710,10 @@ private void SetFrameMetadata(ImageFrameMetadata metadata)
710710
gifMeta.ColorTableMode = GifColorTableMode.Local;
711711

712712
Color[] colorTable = new Color[this.imageDescriptor.LocalColorTableSize];
713-
ref Rgb24 localBase = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<byte, Rgb24>(this.currentLocalColorTable!.GetSpan()[..this.currentLocalColorTableSize]));
713+
ReadOnlySpan<Rgb24> rgbTable = MemoryMarshal.Cast<byte, Rgb24>(this.currentLocalColorTable!.GetSpan()[..this.currentLocalColorTableSize]);
714714
for (int i = 0; i < colorTable.Length; i++)
715715
{
716-
colorTable[i] = new Color(Unsafe.Add(ref localBase, (uint)i));
716+
colorTable[i] = new Color(rgbTable[i]);
717717
}
718718

719719
gifMeta.LocalColorTable = colorTable;
@@ -784,13 +784,14 @@ private void ReadLogicalScreenDescriptorAndGlobalColorTable(BufferedReadStream s
784784
this.globalColorTable = this.memoryAllocator.Allocate<byte>(globalColorTableLength, AllocationOptions.Clean);
785785

786786
// Read the global color table data from the stream and preserve it in the gif metadata
787-
stream.Read(this.globalColorTable.GetSpan());
787+
Span<byte> globalColorTableSpan = this.globalColorTable.GetSpan();
788+
stream.Read(globalColorTableSpan);
788789

789790
Color[] colorTable = new Color[this.logicalScreenDescriptor.GlobalColorTableSize];
790-
ref Rgb24 globalBase = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<byte, Rgb24>(this.globalColorTable.GetSpan()));
791+
ReadOnlySpan<Rgb24> rgbTable = MemoryMarshal.Cast<byte, Rgb24>(globalColorTableSpan);
791792
for (int i = 0; i < colorTable.Length; i++)
792793
{
793-
colorTable[i] = new Color(Unsafe.Add(ref globalBase, (uint)i));
794+
colorTable[i] = new Color(rgbTable[i]);
794795
}
795796

796797
this.gifMetadata.GlobalColorTable = colorTable;

src/ImageSharp/Formats/Gif/GifEncoderCore.cs

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken
135135
byte backgroundIndex = unchecked((byte)transparencyIndex);
136136
if (transparencyIndex == -1)
137137
{
138-
backgroundIndex = gifMetadata.BackgroundColor;
138+
backgroundIndex = gifMetadata.BackgroundColorIndex;
139139
}
140140

141141
// Get the number of bits.
@@ -236,18 +236,19 @@ private void EncodeFirstFrame<TPixel>(
236236
{
237237
this.WriteGraphicalControlExtension(metadata, transparencyIndex, stream);
238238

239-
Buffer2DRegion<byte> region = ((IPixelSource)quantized).PixelBuffer.GetRegion();
239+
Buffer2D<byte> indices = ((IPixelSource)quantized).PixelBuffer;
240+
Rectangle interest = indices.FullRectangle();
240241
bool useLocal = this.colorTableMode == GifColorTableMode.Local || (metadata?.ColorTableMode == GifColorTableMode.Local);
241242
int bitDepth = ColorNumerics.GetBitsNeededForColorDepth(quantized.Palette.Length);
242243

243-
this.WriteImageDescriptor(region.Rectangle, useLocal, bitDepth, stream);
244+
this.WriteImageDescriptor(interest, useLocal, bitDepth, stream);
244245

245246
if (useLocal)
246247
{
247248
this.WriteColorTable(quantized, bitDepth, stream);
248249
}
249250

250-
this.WriteImageData(region, stream, quantized.Palette.Length, transparencyIndex);
251+
this.WriteImageData(indices, interest, stream, quantized.Palette.Length, transparencyIndex);
251252
}
252253

253254
private void EncodeAdditionalFrame<TPixel>(
@@ -322,20 +323,20 @@ private void EncodeAdditionalFrame<TPixel>(
322323
transparencyIndex = GetTransparentIndex(quantized, metadata);
323324

324325
// Trim down the buffer to the minimum size required.
325-
// Buffer2DRegion<byte> region = ((IPixelSource)quantized).PixelBuffer.GetRegion();
326-
Buffer2DRegion<byte> region = TrimTransparentPixels(((IPixelSource)quantized).PixelBuffer, transparencyIndex);
326+
Buffer2D<byte> indices = ((IPixelSource)quantized).PixelBuffer;
327+
Rectangle interest = TrimTransparentPixels(indices, transparencyIndex);
327328

328329
this.WriteGraphicalControlExtension(metadata, transparencyIndex, stream);
329330

330331
int bitDepth = ColorNumerics.GetBitsNeededForColorDepth(quantized.Palette.Length);
331-
this.WriteImageDescriptor(region.Rectangle, useLocal, bitDepth, stream);
332+
this.WriteImageDescriptor(interest, useLocal, bitDepth, stream);
332333

333334
if (useLocal)
334335
{
335336
this.WriteColorTable(quantized, bitDepth, stream);
336337
}
337338

338-
this.WriteImageData(region, stream, quantized.Palette.Length, transparencyIndex);
339+
this.WriteImageData(indices, interest, stream, quantized.Palette.Length, transparencyIndex);
339340
}
340341

341342
private void DeDuplicatePixels<TPixel>(
@@ -399,11 +400,11 @@ private void DeDuplicatePixels<TPixel>(
399400
}
400401
}
401402

402-
private static Buffer2DRegion<byte> TrimTransparentPixels(Buffer2D<byte> buffer, int transparencyIndex)
403+
private static Rectangle TrimTransparentPixels(Buffer2D<byte> buffer, int transparencyIndex)
403404
{
404405
if (transparencyIndex < 0)
405406
{
406-
return buffer.GetRegion();
407+
return buffer.FullRectangle();
407408
}
408409

409410
byte trimmableIndex = unchecked((byte)transparencyIndex);
@@ -596,7 +597,7 @@ private static Buffer2DRegion<byte> TrimTransparentPixels(Buffer2D<byte> buffer,
596597
if (top == bottom || left == right)
597598
{
598599
// The entire image is transparent.
599-
return buffer.GetRegion();
600+
return buffer.FullRectangle();
600601
}
601602

602603
if (!isTransparentRow)
@@ -605,7 +606,7 @@ private static Buffer2DRegion<byte> TrimTransparentPixels(Buffer2D<byte> buffer,
605606
bottom = buffer.Height;
606607
}
607608

608-
return buffer.GetRegion(Rectangle.FromLTRB(left, top, Math.Min(right + 1, buffer.Width), Math.Min(bottom + 1, buffer.Height)));
609+
return Rectangle.FromLTRB(left, top, Math.Min(right + 1, buffer.Width), Math.Min(bottom + 1, buffer.Height));
609610
}
610611

611612
/// <summary>
@@ -923,11 +924,14 @@ private void WriteColorTable<TPixel>(IndexedImageFrame<TPixel> image, int bitDep
923924
/// Writes the image pixel data to the stream.
924925
/// </summary>
925926
/// <param name="indices">The <see cref="Buffer2DRegion{Byte}"/> containing indexed pixels.</param>
927+
/// <param name="interest">The region of interest.</param>
926928
/// <param name="stream">The stream to write to.</param>
927929
/// <param name="paletteLength">The length of the frame color palette.</param>
928930
/// <param name="transparencyIndex">The index of the color used to represent transparency.</param>
929-
private void WriteImageData(Buffer2DRegion<byte> indices, Stream stream, int paletteLength, int transparencyIndex)
931+
private void WriteImageData(Buffer2D<byte> indices, Rectangle interest, Stream stream, int paletteLength, int transparencyIndex)
930932
{
933+
Buffer2DRegion<byte> region = indices.GetRegion(interest);
934+
931935
// Pad the bit depth when required for encoding the image data.
932936
// This is a common trick which allows to use out of range indexes for transparency and avoid allocating a larger color palette
933937
// as decoders skip indexes that are out of range.
@@ -936,6 +940,6 @@ private void WriteImageData(Buffer2DRegion<byte> indices, Stream stream, int pal
936940
: 0;
937941

938942
using LzwEncoder encoder = new(this.memoryAllocator, ColorNumerics.GetBitsNeededForColorDepth(paletteLength + padding));
939-
encoder.Encode(indices, stream);
943+
encoder.Encode(region, stream);
940944
}
941945
}

src/ImageSharp/Formats/Gif/GifFrameMetadata.cs

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

4+
using SixLabors.ImageSharp.PixelFormats;
5+
46
namespace SixLabors.ImageSharp.Formats.Gif;
57

68
/// <summary>
@@ -41,6 +43,7 @@ private GifFrameMetadata(GifFrameMetadata other)
4143

4244
/// <summary>
4345
/// Gets or sets the local color table, if any.
46+
/// The underlying pixel format is represented by <see cref="Rgb24"/>.
4447
/// </summary>
4548
public ReadOnlyMemory<Color>? LocalColorTable { get; set; }
4649

src/ImageSharp/Formats/Gif/GifMetadata.cs

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

4+
using SixLabors.ImageSharp.PixelFormats;
5+
46
namespace SixLabors.ImageSharp.Formats.Gif;
57

68
/// <summary>
@@ -23,7 +25,7 @@ private GifMetadata(GifMetadata other)
2325
{
2426
this.RepeatCount = other.RepeatCount;
2527
this.ColorTableMode = other.ColorTableMode;
26-
this.BackgroundColor = other.BackgroundColor;
28+
this.BackgroundColorIndex = other.BackgroundColorIndex;
2729

2830
if (other.GlobalColorTable?.Length > 0)
2931
{
@@ -51,14 +53,15 @@ private GifMetadata(GifMetadata other)
5153

5254
/// <summary>
5355
/// Gets or sets the global color table, if any.
56+
/// The underlying pixel format is represented by <see cref="Rgb24"/>.
5457
/// </summary>
5558
public ReadOnlyMemory<Color>? GlobalColorTable { get; set; }
5659

5760
/// <summary>
5861
/// Gets or sets the index at the <see cref="GlobalColorTable"/> for the background color.
5962
/// The background color is the color used for those pixels on the screen that are not covered by an image.
6063
/// </summary>
61-
public byte BackgroundColor { get; set; }
64+
public byte BackgroundColorIndex { get; set; }
6265

6366
/// <summary>
6467
/// Gets or sets the collection of comments about the graphics, credits, descriptions or any

src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBitReader.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,12 @@ public bool FindNextMarker()
212212
private int ReadStream()
213213
{
214214
int value = this.badData ? 0 : this.stream.ReadByte();
215-
if (value == -1)
215+
216+
// We've encountered the end of the file stream which means there's no EOI marker or the marker has been read
217+
// during decoding of the SOS marker.
218+
// When reading individual bits 'badData' simply means we have hit a marker, When data is '0' and the stream is exhausted
219+
// we know we have hit the EOI and completed decoding the scan buffer.
220+
if (value == -1 || (this.badData && this.data == 0 && this.stream.Position >= this.stream.Length))
216221
{
217222
// We've encountered the end of the file stream which means there's no EOI marker
218223
// in the image or the SOS marker has the wrong dimensions set.

src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken
158158
long ifdMarker = WriteHeader(writer, buffer);
159159

160160
Image<TPixel> metadataImage = image;
161+
161162
foreach (ImageFrame<TPixel> frame in image.Frames)
162163
{
163164
cancellationToken.ThrowIfCancellationRequested();
@@ -236,9 +237,13 @@ private long WriteFrame<TPixel>(
236237

237238
if (image != null)
238239
{
240+
// Write the metadata for the root image
239241
entriesCollector.ProcessMetadata(image, this.skipMetadata);
240242
}
241243

244+
// Write the metadata for the frame
245+
entriesCollector.ProcessMetadata(frame, this.skipMetadata);
246+
242247
entriesCollector.ProcessFrameInfo(frame, imageMetadata);
243248
entriesCollector.ProcessImageFormat(this);
244249

0 commit comments

Comments
 (0)