Skip to content

Commit f774f45

Browse files
Merge branch 'main' into mk/optimize-PNG-encoding
2 parents 0b1cf1c + 14418c1 commit f774f45

File tree

59 files changed

+1157
-249
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+1157
-249
lines changed

.github/ISSUE_TEMPLATE/commercial-bug-report.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,20 @@ description: |
44
Please contact [email protected] for issues requiring private support.
55
labels: ["commercial", "needs triage"]
66
body:
7+
- type: checkboxes
8+
attributes:
9+
label: Prerequisites
10+
options:
11+
- label: I have bought a Commercial License
12+
required: true
13+
- label: I have written a descriptive issue title
14+
required: true
15+
- label: I have verified that I am running the latest version of ImageSharp
16+
required: true
17+
- label: I have verified if the problem exist in both `DEBUG` and `RELEASE` mode
18+
required: true
19+
- label: I have searched [open](https://github.com/SixLabors/ImageSharp/issues) and [closed](https://github.com/SixLabors/ImageSharp/issues?q=is%3Aissue+is%3Aclosed) issues to ensure it has not already been reported
20+
required: true
721
- type: input
822
attributes:
923
label: ImageSharp version

.github/ISSUE_TEMPLATE/oss-bug-report.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,18 @@ name: "OSS : Bug Report"
22
description: Create a report to help us improve the project. OSS Issues are not guaranteed to be triaged.
33
labels: ["needs triage"]
44
body:
5+
- type: checkboxes
6+
attributes:
7+
label: Prerequisites
8+
options:
9+
- label: I have written a descriptive issue title
10+
required: true
11+
- label: I have verified that I am running the latest version of ImageSharp
12+
required: true
13+
- label: I have verified if the problem exist in both `DEBUG` and `RELEASE` mode
14+
required: true
15+
- label: I have searched [open](https://github.com/SixLabors/ImageSharp/issues) and [closed](https://github.com/SixLabors/ImageSharp/issues?q=is%3Aissue+is%3Aclosed) issues to ensure it has not already been reported
16+
required: true
517
- type: input
618
attributes:
719
label: ImageSharp version

ImageSharp.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Gif", "Gif", "{EE3FB0B3-1C3
142142
EndProject
143143
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "issues", "issues", "{BF8DFDC1-CEE5-4A37-B216-D3085360C776}"
144144
ProjectSection(SolutionItems) = preProject
145+
tests\Images\Input\Gif\issues\bugzilla-55918.gif = tests\Images\Input\Gif\issues\bugzilla-55918.gif
146+
tests\Images\Input\Gif\issues\issue1505_argumentoutofrange.png = tests\Images\Input\Gif\issues\issue1505_argumentoutofrange.png
147+
tests\Images\Input\Gif\issues\issue1530.gif = tests\Images\Input\Gif\issues\issue1530.gif
148+
tests\Images\Input\Gif\issues\issue1668_invalidcolorindex.gif = tests\Images\Input\Gif\issues\issue1668_invalidcolorindex.gif
149+
tests\Images\Input\Gif\issues\issue1962_tiniest_gif_1st.gif = tests\Images\Input\Gif\issues\issue1962_tiniest_gif_1st.gif
150+
tests\Images\Input\Gif\issues\issue2012_drona1.gif = tests\Images\Input\Gif\issues\issue2012_drona1.gif
151+
tests\Images\Input\Gif\issues\issue2012_Stronghold-Crusader-Extreme-Cover.gif = tests\Images\Input\Gif\issues\issue2012_Stronghold-Crusader-Extreme-Cover.gif
145152
tests\Images\Input\Gif\issues\issue403_baddescriptorwidth.gif = tests\Images\Input\Gif\issues\issue403_baddescriptorwidth.gif
146153
tests\Images\Input\Gif\issues\issue405_badappextlength252-2.gif = tests\Images\Input\Gif\issues\issue405_badappextlength252-2.gif
147154
tests\Images\Input\Gif\issues\issue405_badappextlength252.gif = tests\Images\Input\Gif\issues\issue405_badappextlength252.gif

src/ImageSharp/Common/Extensions/StreamExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp
1313
internal static class StreamExtensions
1414
{
1515
/// <summary>
16-
/// Writes data from a stream into the provided buffer.
16+
/// Writes data from a stream from the provided buffer.
1717
/// </summary>
1818
/// <param name="stream">The stream.</param>
1919
/// <param name="buffer">The buffer.</param>

src/ImageSharp/Compression/Zlib/Adler32.cs

Lines changed: 129 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System;
55
using System.Runtime.CompilerServices;
6+
using System.Runtime.InteropServices;
67
#if SUPPORTS_RUNTIME_INTRINSICS
78
using System.Runtime.Intrinsics;
89
using System.Runtime.Intrinsics.X86;
@@ -31,6 +32,8 @@ internal static class Adler32
3132
#if SUPPORTS_RUNTIME_INTRINSICS
3233
private const int MinBufferSize = 64;
3334

35+
private const int BlockSize = 1 << 5;
36+
3437
// The C# compiler emits this as a compile-time constant embedded in the PE file.
3538
private static ReadOnlySpan<byte> Tap1Tap2 => new byte[]
3639
{
@@ -63,6 +66,11 @@ public static uint Calculate(uint adler, ReadOnlySpan<byte> buffer)
6366
}
6467

6568
#if SUPPORTS_RUNTIME_INTRINSICS
69+
if (Avx2.IsSupported && buffer.Length >= MinBufferSize)
70+
{
71+
return CalculateAvx2(adler, buffer);
72+
}
73+
6674
if (Ssse3.IsSupported && buffer.Length >= MinBufferSize)
6775
{
6876
return CalculateSse(adler, buffer);
@@ -83,19 +91,15 @@ private static unsafe uint CalculateSse(uint adler, ReadOnlySpan<byte> buffer)
8391
uint s2 = (adler >> 16) & 0xFFFF;
8492

8593
// Process the data in blocks.
86-
const int BLOCK_SIZE = 1 << 5;
87-
8894
uint length = (uint)buffer.Length;
89-
uint blocks = length / BLOCK_SIZE;
90-
length -= blocks * BLOCK_SIZE;
95+
uint blocks = length / BlockSize;
96+
length -= blocks * BlockSize;
9197

92-
int index = 0;
93-
fixed (byte* bufferPtr = buffer)
98+
fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer))
9499
{
95-
fixed (byte* tapPtr = Tap1Tap2)
100+
fixed (byte* tapPtr = &MemoryMarshal.GetReference(Tap1Tap2))
96101
{
97-
index += (int)blocks * BLOCK_SIZE;
98-
var localBufferPtr = bufferPtr;
102+
byte* localBufferPtr = bufferPtr;
99103

100104
// _mm_setr_epi8 on x86
101105
Vector128<sbyte> tap1 = Sse2.LoadVector128((sbyte*)tapPtr);
@@ -105,7 +109,7 @@ private static unsafe uint CalculateSse(uint adler, ReadOnlySpan<byte> buffer)
105109

106110
while (blocks > 0)
107111
{
108-
uint n = NMAX / BLOCK_SIZE; /* The NMAX constraint. */
112+
uint n = NMAX / BlockSize; /* The NMAX constraint. */
109113
if (n > blocks)
110114
{
111115
n = blocks;
@@ -138,7 +142,7 @@ private static unsafe uint CalculateSse(uint adler, ReadOnlySpan<byte> buffer)
138142
Vector128<short> mad2 = Ssse3.MultiplyAddAdjacent(bytes2, tap2);
139143
v_s2 = Sse2.Add(v_s2, Sse2.MultiplyAddAdjacent(mad2, ones).AsUInt32());
140144

141-
localBufferPtr += BLOCK_SIZE;
145+
localBufferPtr += BlockSize;
142146
}
143147
while (--n > 0);
144148

@@ -164,45 +168,127 @@ private static unsafe uint CalculateSse(uint adler, ReadOnlySpan<byte> buffer)
164168

165169
if (length > 0)
166170
{
167-
if (length >= 16)
168-
{
169-
s2 += s1 += localBufferPtr[0];
170-
s2 += s1 += localBufferPtr[1];
171-
s2 += s1 += localBufferPtr[2];
172-
s2 += s1 += localBufferPtr[3];
173-
s2 += s1 += localBufferPtr[4];
174-
s2 += s1 += localBufferPtr[5];
175-
s2 += s1 += localBufferPtr[6];
176-
s2 += s1 += localBufferPtr[7];
177-
s2 += s1 += localBufferPtr[8];
178-
s2 += s1 += localBufferPtr[9];
179-
s2 += s1 += localBufferPtr[10];
180-
s2 += s1 += localBufferPtr[11];
181-
s2 += s1 += localBufferPtr[12];
182-
s2 += s1 += localBufferPtr[13];
183-
s2 += s1 += localBufferPtr[14];
184-
s2 += s1 += localBufferPtr[15];
185-
186-
localBufferPtr += 16;
187-
length -= 16;
188-
}
171+
HandleLeftOver(localBufferPtr, length, ref s1, ref s2);
172+
}
189173

190-
while (length-- > 0)
191-
{
192-
s2 += s1 += *localBufferPtr++;
193-
}
174+
return s1 | (s2 << 16);
175+
}
176+
}
177+
}
194178

195-
if (s1 >= BASE)
196-
{
197-
s1 -= BASE;
198-
}
179+
// Based on: https://github.com/zlib-ng/zlib-ng/blob/develop/arch/x86/adler32_avx2.c
180+
[MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)]
181+
public static unsafe uint CalculateAvx2(uint adler, ReadOnlySpan<byte> buffer)
182+
{
183+
uint s1 = adler & 0xFFFF;
184+
uint s2 = (adler >> 16) & 0xFFFF;
185+
uint length = (uint)buffer.Length;
199186

200-
s2 %= BASE;
187+
fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer))
188+
{
189+
byte* localBufferPtr = bufferPtr;
190+
191+
Vector256<byte> zero = Vector256<byte>.Zero;
192+
var dot3v = Vector256.Create((short)1);
193+
var dot2v = Vector256.Create(32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1);
194+
195+
// Process n blocks of data. At most NMAX data bytes can be
196+
// processed before s2 must be reduced modulo BASE.
197+
var vs1 = Vector256.CreateScalar(s1);
198+
var vs2 = Vector256.CreateScalar(s2);
199+
200+
while (length >= 32)
201+
{
202+
int k = length < NMAX ? (int)length : (int)NMAX;
203+
k -= k % 32;
204+
length -= (uint)k;
205+
206+
Vector256<uint> vs10 = vs1;
207+
Vector256<uint> vs3 = Vector256<uint>.Zero;
208+
209+
while (k >= 32)
210+
{
211+
// Load 32 input bytes.
212+
Vector256<byte> block = Avx.LoadVector256(localBufferPtr);
213+
214+
// Sum of abs diff, resulting in 2 x int32's
215+
Vector256<ushort> vs1sad = Avx2.SumAbsoluteDifferences(block, zero);
216+
217+
vs1 = Avx2.Add(vs1, vs1sad.AsUInt32());
218+
vs3 = Avx2.Add(vs3, vs10);
219+
220+
// sum 32 uint8s to 16 shorts.
221+
Vector256<short> vshortsum2 = Avx2.MultiplyAddAdjacent(block, dot2v);
222+
223+
// sum 16 shorts to 8 uint32s.
224+
Vector256<int> vsum2 = Avx2.MultiplyAddAdjacent(vshortsum2, dot3v);
225+
226+
vs2 = Avx2.Add(vsum2.AsUInt32(), vs2);
227+
vs10 = vs1;
228+
229+
localBufferPtr += BlockSize;
230+
k -= 32;
201231
}
202232

203-
return s1 | (s2 << 16);
233+
// Defer the multiplication with 32 to outside of the loop.
234+
vs3 = Avx2.ShiftLeftLogical(vs3, 5);
235+
vs2 = Avx2.Add(vs2, vs3);
236+
237+
s1 = (uint)Numerics.EvenReduceSum(vs1.AsInt32());
238+
s2 = (uint)Numerics.ReduceSum(vs2.AsInt32());
239+
240+
s1 %= BASE;
241+
s2 %= BASE;
242+
243+
vs1 = Vector256.CreateScalar(s1);
244+
vs2 = Vector256.CreateScalar(s2);
204245
}
246+
247+
if (length > 0)
248+
{
249+
HandleLeftOver(localBufferPtr, length, ref s1, ref s2);
250+
}
251+
252+
return s1 | (s2 << 16);
253+
}
254+
}
255+
256+
private static unsafe void HandleLeftOver(byte* localBufferPtr, uint length, ref uint s1, ref uint s2)
257+
{
258+
if (length >= 16)
259+
{
260+
s2 += s1 += localBufferPtr[0];
261+
s2 += s1 += localBufferPtr[1];
262+
s2 += s1 += localBufferPtr[2];
263+
s2 += s1 += localBufferPtr[3];
264+
s2 += s1 += localBufferPtr[4];
265+
s2 += s1 += localBufferPtr[5];
266+
s2 += s1 += localBufferPtr[6];
267+
s2 += s1 += localBufferPtr[7];
268+
s2 += s1 += localBufferPtr[8];
269+
s2 += s1 += localBufferPtr[9];
270+
s2 += s1 += localBufferPtr[10];
271+
s2 += s1 += localBufferPtr[11];
272+
s2 += s1 += localBufferPtr[12];
273+
s2 += s1 += localBufferPtr[13];
274+
s2 += s1 += localBufferPtr[14];
275+
s2 += s1 += localBufferPtr[15];
276+
277+
localBufferPtr += 16;
278+
length -= 16;
205279
}
280+
281+
while (length-- > 0)
282+
{
283+
s2 += s1 += *localBufferPtr++;
284+
}
285+
286+
if (s1 >= BASE)
287+
{
288+
s1 -= BASE;
289+
}
290+
291+
s2 %= BASE;
206292
}
207293
#endif
208294

src/ImageSharp/Formats/Gif/GifDecoder.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
using System.Threading;
66
using System.Threading.Tasks;
77
using SixLabors.ImageSharp.IO;
8-
using SixLabors.ImageSharp.Memory;
98
using SixLabors.ImageSharp.Metadata;
109
using SixLabors.ImageSharp.PixelFormats;
1110

src/ImageSharp/Formats/Gif/GifDecoderCore.cs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -265,10 +265,14 @@ private void ReadApplicationExtension()
265265
this.stream.Read(this.buffer, 0, GifConstants.ApplicationBlockSize);
266266
bool isXmp = this.buffer.AsSpan().StartsWith(GifConstants.XmpApplicationIdentificationBytes);
267267

268-
if (isXmp)
268+
if (isXmp && !this.IgnoreMetadata)
269269
{
270-
var extension = GifXmpApplicationExtension.Read(this.stream);
271-
this.metadata.XmpProfile = new XmpProfile(extension.Data);
270+
var extension = GifXmpApplicationExtension.Read(this.stream, this.MemoryAllocator);
271+
if (extension.Data.Length > 0)
272+
{
273+
this.metadata.XmpProfile = new XmpProfile(extension.Data);
274+
}
275+
272276
return;
273277
}
274278
else
@@ -374,8 +378,8 @@ private void ReadFrame<TPixel>(ref Image<TPixel> image, ref ImageFrame<TPixel> p
374378
}
375379

376380
indices = this.Configuration.MemoryAllocator.Allocate2D<byte>(this.imageDescriptor.Width, this.imageDescriptor.Height, AllocationOptions.Clean);
377-
378381
this.ReadFrameIndices(indices);
382+
379383
Span<byte> rawColorTable = default;
380384
if (localColorTable != null)
381385
{
@@ -406,9 +410,9 @@ private void ReadFrame<TPixel>(ref Image<TPixel> image, ref ImageFrame<TPixel> p
406410
[MethodImpl(MethodImplOptions.AggressiveInlining)]
407411
private void ReadFrameIndices(Buffer2D<byte> indices)
408412
{
409-
int dataSize = this.stream.ReadByte();
413+
int minCodeSize = this.stream.ReadByte();
410414
using var lzwDecoder = new LzwDecoder(this.Configuration.MemoryAllocator, this.stream);
411-
lzwDecoder.DecodePixels(dataSize, indices);
415+
lzwDecoder.DecodePixels(minCodeSize, indices);
412416
}
413417

414418
/// <summary>

0 commit comments

Comments
 (0)