Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 62da42d

Browse filesBrowse files
Merge pull request #2894 from SixLabors/js/fix-2866-v4
V4 : Fix GIF, PNG, and WEBP Edge Case Handling
2 parents ba61e04 + 5f971c7 commit 62da42d
Copy full SHA for 62da42d

File tree

Expand file treeCollapse file tree

215 files changed

+3331
-1449
lines changed
Filter options

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Dismiss banner
Expand file treeCollapse file tree

215 files changed

+3331
-1449
lines changed

‎src/ImageSharp/Advanced/AotCompilerTools.cs

Copy file name to clipboardExpand all lines: src/ImageSharp/Advanced/AotCompilerTools.cs
+16-1Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,10 +138,11 @@ private static void Seed<TPixel>()
138138
AotCompileResamplers<TPixel>();
139139
AotCompileQuantizers<TPixel>();
140140
AotCompilePixelSamplingStrategys<TPixel>();
141+
AotCompilePixelMaps<TPixel>();
141142
AotCompileDithers<TPixel>();
142143
AotCompileMemoryManagers<TPixel>();
143144

144-
Unsafe.SizeOf<TPixel>();
145+
_ = Unsafe.SizeOf<TPixel>();
145146

146147
// TODO: Do the discovery work to figure out what works and what doesn't.
147148
}
@@ -514,6 +515,20 @@ private static void AotCompilePixelSamplingStrategys<TPixel>()
514515
default(ExtensivePixelSamplingStrategy).EnumeratePixelRegions(default(ImageFrame<TPixel>));
515516
}
516517

518+
/// <summary>
519+
/// This method pre-seeds the all <see cref="IColorIndexCache{T}" /> in the AoT compiler.
520+
/// </summary>
521+
/// <typeparam name="TPixel">The pixel format.</typeparam>
522+
[Preserve]
523+
private static void AotCompilePixelMaps<TPixel>()
524+
where TPixel : unmanaged, IPixel<TPixel>
525+
{
526+
default(EuclideanPixelMap<TPixel, HybridCache>).GetClosestColor(default, out _);
527+
default(EuclideanPixelMap<TPixel, AccurateCache>).GetClosestColor(default, out _);
528+
default(EuclideanPixelMap<TPixel, CoarseCache>).GetClosestColor(default, out _);
529+
default(EuclideanPixelMap<TPixel, NullCache>).GetClosestColor(default, out _);
530+
}
531+
517532
/// <summary>
518533
/// This method pre-seeds the all <see cref="IDither" /> in the AoT compiler.
519534
/// </summary>

‎src/ImageSharp/Common/InlineArray.cs

Copy file name to clipboard
+38Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Six Labors Split License.
3+
4+
// <auto-generated />
5+
6+
using System;
7+
using System.Runtime.CompilerServices;
8+
9+
namespace SixLabors.ImageSharp;
10+
11+
/// <summary>
12+
/// Represents a safe, fixed sized buffer of 4 elements.
13+
/// </summary>
14+
[InlineArray(4)]
15+
internal struct InlineArray4<T>
16+
{
17+
private T t;
18+
}
19+
20+
/// <summary>
21+
/// Represents a safe, fixed sized buffer of 8 elements.
22+
/// </summary>
23+
[InlineArray(8)]
24+
internal struct InlineArray8<T>
25+
{
26+
private T t;
27+
}
28+
29+
/// <summary>
30+
/// Represents a safe, fixed sized buffer of 16 elements.
31+
/// </summary>
32+
[InlineArray(16)]
33+
internal struct InlineArray16<T>
34+
{
35+
private T t;
36+
}
37+
38+

‎src/ImageSharp/Common/InlineArray.tt

Copy file name to clipboard
+38Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<#@ template debug="false" hostspecific="false" language="C#" #>
2+
<#@ assembly name="System.Core" #>
3+
<#@ import namespace="System.Linq" #>
4+
<#@ import namespace="System.Text" #>
5+
<#@ import namespace="System.Collections.Generic" #>
6+
// Copyright (c) Six Labors.
7+
// Licensed under the Six Labors Split License.
8+
9+
// <auto-generated />
10+
11+
using System;
12+
using System.Runtime.CompilerServices;
13+
14+
namespace SixLabors.ImageSharp;
15+
16+
<#GenerateInlineArrays();#>
17+
18+
<#+
19+
private static int[] Lengths = new int[] {4, 8, 16 };
20+
21+
void GenerateInlineArrays()
22+
{
23+
foreach (int length in Lengths)
24+
{
25+
#>
26+
/// <summary>
27+
/// Represents a safe, fixed sized buffer of <#=length#> elements.
28+
/// </summary>
29+
[InlineArray(<#=length#>)]
30+
internal struct InlineArray<#=length#><T>
31+
{
32+
private T t;
33+
}
34+
35+
<#+
36+
}
37+
}
38+
#>

‎src/ImageSharp/Formats/AlphaAwareImageEncoder.cs

Copy file name to clipboardExpand all lines: src/ImageSharp/Formats/AlphaAwareImageEncoder.cs
+4Lines changed: 4 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.Processing.Processors.Quantization;
5+
46
namespace SixLabors.ImageSharp.Formats;
57

68
/// <summary>
@@ -10,6 +12,8 @@ public abstract class AlphaAwareImageEncoder : ImageEncoder
1012
{
1113
/// <summary>
1214
/// Gets or initializes the mode that determines how transparent pixels are handled during encoding.
15+
/// This overrides any other settings that may affect the encoding of transparent pixels
16+
/// including those passed via <see cref="QuantizerOptions"/>.
1317
/// </summary>
1418
public TransparentColorMode TransparentColorMode { get; init; }
1519
}

‎src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs

Copy file name to clipboardExpand all lines: src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
+5-2Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -362,10 +362,13 @@ private void WriteImage<TPixel>(
362362
ImageFrame<TPixel>? clonedFrame = null;
363363
try
364364
{
365-
if (EncodingUtilities.ShouldClearTransparentPixels<TPixel>(this.transparentColorMode))
365+
// No need to clone when quantizing. The quantizer will do it for us.
366+
// TODO: We should really try to avoid the clone entirely.
367+
int bpp = this.bitsPerPixel != null ? (int)this.bitsPerPixel : 32;
368+
if (bpp > 8 && EncodingUtilities.ShouldReplaceTransparentPixels<TPixel>(this.transparentColorMode))
366369
{
367370
clonedFrame = image.Frames.RootFrame.Clone();
368-
EncodingUtilities.ClearTransparentPixels(clonedFrame, Color.Transparent);
371+
EncodingUtilities.ReplaceTransparentPixels(clonedFrame);
369372
}
370373

371374
ImageFrame<TPixel> encodingFrame = clonedFrame ?? image.Frames.RootFrame;

‎src/ImageSharp/Formats/Bmp/BmpMetadata.cs

Copy file name to clipboardExpand all lines: src/ImageSharp/Formats/Bmp/BmpMetadata.cs
+1-2Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,5 @@ public FormatConnectingMetadata ToFormatConnectingMetadata()
158158
/// <inheritdoc/>
159159
public void AfterImageApply<TPixel>(Image<TPixel> destination)
160160
where TPixel : unmanaged, IPixel<TPixel>
161-
{
162-
}
161+
=> this.ColorTable = null;
163162
}

‎src/ImageSharp/Formats/Cur/CurFrameMetadata.cs

Copy file name to clipboardExpand all lines: src/ImageSharp/Formats/Cur/CurFrameMetadata.cs
+1-2Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,6 @@ public static CurFrameMetadata FromFormatConnectingFrameMetadata(FormatConnectin
104104
Compression = compression,
105105
EncodingWidth = ClampEncodingDimension(metadata.EncodingWidth),
106106
EncodingHeight = ClampEncodingDimension(metadata.EncodingHeight),
107-
ColorTable = compression == IconFrameCompression.Bmp ? metadata.ColorTable : null
108107
};
109108
}
110109

@@ -113,7 +112,6 @@ public FormatConnectingFrameMetadata ToFormatConnectingFrameMetadata()
113112
=> new()
114113
{
115114
PixelTypeInfo = this.GetPixelTypeInfo(),
116-
ColorTable = this.ColorTable,
117115
EncodingWidth = this.EncodingWidth,
118116
EncodingHeight = this.EncodingHeight
119117
};
@@ -126,6 +124,7 @@ public void AfterFrameApply<TPixel>(ImageFrame<TPixel> source, ImageFrame<TPixel
126124
float ratioY = destination.Height / (float)source.Height;
127125
this.EncodingWidth = ScaleEncodingDimension(this.EncodingWidth, destination.Width, ratioX);
128126
this.EncodingHeight = ScaleEncodingDimension(this.EncodingHeight, destination.Height, ratioY);
127+
this.ColorTable = null;
129128
}
130129

131130
/// <inheritdoc/>

‎src/ImageSharp/Formats/Cur/CurMetadata.cs

Copy file name to clipboardExpand all lines: src/ImageSharp/Formats/Cur/CurMetadata.cs
+3-6Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,7 @@ public static CurMetadata FromFormatConnectingMetadata(FormatConnectingMetadata
7171
return new CurMetadata
7272
{
7373
BmpBitsPerPixel = bbpp,
74-
Compression = compression,
75-
ColorTable = compression == IconFrameCompression.Bmp ? metadata.ColorTable : null
74+
Compression = compression
7675
};
7776
}
7877

@@ -145,15 +144,13 @@ public FormatConnectingMetadata ToFormatConnectingMetadata()
145144
EncodingType = this.Compression == IconFrameCompression.Bmp && this.BmpBitsPerPixel <= BmpBitsPerPixel.Bit8
146145
? EncodingType.Lossy
147146
: EncodingType.Lossless,
148-
PixelTypeInfo = this.GetPixelTypeInfo(),
149-
ColorTable = this.ColorTable
147+
PixelTypeInfo = this.GetPixelTypeInfo()
150148
};
151149

152150
/// <inheritdoc/>
153151
public void AfterImageApply<TPixel>(Image<TPixel> destination)
154152
where TPixel : unmanaged, IPixel<TPixel>
155-
{
156-
}
153+
=> this.ColorTable = null;
157154

158155
/// <inheritdoc/>
159156
IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone();

‎src/ImageSharp/Formats/EncodingUtilities.cs

Copy file name to clipboardExpand all lines: src/ImageSharp/Formats/EncodingUtilities.cs
+105-33Lines changed: 105 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
using System.Buffers;
55
using System.Numerics;
6+
using System.Runtime.CompilerServices;
7+
using System.Runtime.InteropServices;
68
using System.Runtime.Intrinsics;
79
using SixLabors.ImageSharp.Memory;
810
using SixLabors.ImageSharp.PixelFormats;
@@ -14,62 +16,132 @@ namespace SixLabors.ImageSharp.Formats;
1416
/// </summary>
1517
internal static class EncodingUtilities
1618
{
17-
public static bool ShouldClearTransparentPixels<TPixel>(TransparentColorMode mode)
19+
/// <summary>
20+
/// Determines if transparent pixels can be replaced based on the specified color mode and pixel type.
21+
/// </summary>
22+
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
23+
/// <param name="mode">Indicates the color mode used to assess the ability to replace transparent pixels.</param>
24+
/// <returns>Returns true if transparent pixels can be replaced; otherwise, false.</returns>
25+
public static bool ShouldReplaceTransparentPixels<TPixel>(TransparentColorMode mode)
26+
where TPixel : unmanaged, IPixel<TPixel>
27+
=> mode == TransparentColorMode.Clear && TPixel.GetPixelTypeInfo().AlphaRepresentation == PixelAlphaRepresentation.Unassociated;
28+
29+
/// <summary>
30+
/// Replaces pixels with a transparent alpha component with fully transparent pixels.
31+
/// </summary>
32+
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
33+
/// <param name="frame">The <see cref="ImageFrame{TPixel}"/> where the transparent pixels will be changed.</param>
34+
public static void ReplaceTransparentPixels<TPixel>(ImageFrame<TPixel> frame)
1835
where TPixel : unmanaged, IPixel<TPixel>
19-
=> mode == TransparentColorMode.Clear &&
20-
TPixel.GetPixelTypeInfo().AlphaRepresentation == PixelAlphaRepresentation.Unassociated;
36+
=> ReplaceTransparentPixels(frame.Configuration, frame.PixelBuffer);
2137

2238
/// <summary>
23-
/// Convert transparent pixels, to pixels represented by <paramref name="color"/>, which can yield
24-
/// to better compression in some cases.
39+
/// Replaces pixels with a transparent alpha component with fully transparent pixels.
2540
/// </summary>
2641
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
27-
/// <param name="clone">The cloned <see cref="ImageFrame{TPixel}"/> where the transparent pixels will be changed.</param>
28-
/// <param name="color">The color to replace transparent pixels with.</param>
29-
public static void ClearTransparentPixels<TPixel>(ImageFrame<TPixel> clone, Color color)
42+
/// <param name="configuration">The configuration.</param>
43+
/// <param name="buffer">The <see cref="Buffer2D{TPixel}"/> where the transparent pixels will be changed.</param>
44+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
45+
public static void ReplaceTransparentPixels<TPixel>(Configuration configuration, Buffer2D<TPixel> buffer)
3046
where TPixel : unmanaged, IPixel<TPixel>
3147
{
32-
Buffer2DRegion<TPixel> buffer = clone.PixelBuffer.GetRegion();
33-
ClearTransparentPixels(clone.Configuration, ref buffer, color);
48+
Buffer2DRegion<TPixel> region = buffer.GetRegion();
49+
ReplaceTransparentPixels(configuration, in region);
3450
}
3551

3652
/// <summary>
37-
/// Convert transparent pixels, to pixels represented by <paramref name="color"/>, which can yield
38-
/// to better compression in some cases.
53+
/// Replaces pixels with a transparent alpha component with fully transparent pixels.
3954
/// </summary>
4055
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
4156
/// <param name="configuration">The configuration.</param>
42-
/// <param name="clone">The cloned <see cref="Buffer2DRegion{T}"/> where the transparent pixels will be changed.</param>
43-
/// <param name="color">The color to replace transparent pixels with.</param>
44-
public static void ClearTransparentPixels<TPixel>(
57+
/// <param name="region">The <see cref="Buffer2DRegion{T}"/> where the transparent pixels will be changed.</param>
58+
public static void ReplaceTransparentPixels<TPixel>(
4559
Configuration configuration,
46-
ref Buffer2DRegion<TPixel> clone,
47-
Color color)
60+
in Buffer2DRegion<TPixel> region)
4861
where TPixel : unmanaged, IPixel<TPixel>
4962
{
50-
using IMemoryOwner<Vector4> vectors = configuration.MemoryAllocator.Allocate<Vector4>(clone.Width);
63+
using IMemoryOwner<Vector4> vectors = configuration.MemoryAllocator.Allocate<Vector4>(region.Width);
5164
Span<Vector4> vectorsSpan = vectors.GetSpan();
52-
Vector4 replacement = color.ToScaledVector4();
53-
for (int y = 0; y < clone.Height; y++)
65+
for (int y = 0; y < region.Height; y++)
5466
{
55-
Span<TPixel> span = clone.DangerousGetRowSpan(y);
67+
Span<TPixel> span = region.DangerousGetRowSpan(y);
5668
PixelOperations<TPixel>.Instance.ToVector4(configuration, span, vectorsSpan, PixelConversionModifiers.Scale);
57-
ClearTransparentPixelRow(vectorsSpan, replacement);
69+
ReplaceTransparentPixels(vectorsSpan);
5870
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, vectorsSpan, span, PixelConversionModifiers.Scale);
5971
}
6072
}
6173

62-
private static void ClearTransparentPixelRow(
63-
Span<Vector4> vectorsSpan,
64-
Vector4 replacement)
74+
/// <summary>
75+
/// Replaces pixels with a transparent alpha component with fully transparent pixels.
76+
/// </summary>
77+
/// <param name="source">A span of color vectors that will be checked for transparency and potentially modified.</param>
78+
public static void ReplaceTransparentPixels(Span<Vector4> source)
6579
{
66-
if (Vector128.IsHardwareAccelerated)
80+
if (Vector512.IsHardwareAccelerated && source.Length >= 4)
81+
{
82+
Span<Vector512<float>> source512 = MemoryMarshal.Cast<Vector4, Vector512<float>>(source);
83+
for (int i = 0; i < source512.Length; i++)
84+
{
85+
ref Vector512<float> v = ref source512[i];
86+
87+
// Do `vector < threshold`
88+
Vector512<float> mask = Vector512.Equals(v, Vector512<float>.Zero);
89+
90+
// Replicate the result for W to all elements (is AllBitsSet if the W was 0 and Zero otherwise)
91+
mask = Vector512.Shuffle(mask, Vector512.Create(3, 3, 3, 3, 7, 7, 7, 7, 11, 11, 11, 11, 15, 15, 15, 15));
92+
93+
// Use the mask to select the replacement vector
94+
// (replacement & mask) | (v512 & ~mask)
95+
v = Vector512.ConditionalSelect(mask, Vector512<float>.Zero, v);
96+
}
97+
98+
int m = Numerics.Modulo4(source.Length);
99+
if (m != 0)
100+
{
101+
for (int i = source.Length - m; i < source.Length; i++)
102+
{
103+
if (source[i].W == 0)
104+
{
105+
source[i] = Vector4.Zero;
106+
}
107+
}
108+
}
109+
}
110+
else if (Vector256.IsHardwareAccelerated && source.Length >= 2)
67111
{
68-
Vector128<float> replacement128 = replacement.AsVector128();
112+
Span<Vector256<float>> source256 = MemoryMarshal.Cast<Vector4, Vector256<float>>(source);
113+
for (int i = 0; i < source256.Length; i++)
114+
{
115+
ref Vector256<float> v = ref source256[i];
116+
117+
// Do `vector < threshold`
118+
Vector256<float> mask = Vector256.Equals(v, Vector256<float>.Zero);
119+
120+
// Replicate the result for W to all elements (is AllBitsSet if the W was 0 and Zero otherwise)
121+
mask = Vector256.Shuffle(mask, Vector256.Create(3, 3, 3, 3, 7, 7, 7, 7));
69122

70-
for (int i = 0; i < vectorsSpan.Length; i++)
123+
// Use the mask to select the replacement vector
124+
// (replacement & mask) | (v256 & ~mask)
125+
v = Vector256.ConditionalSelect(mask, Vector256<float>.Zero, v);
126+
}
127+
128+
int m = Numerics.Modulo2(source.Length);
129+
if (m != 0)
130+
{
131+
for (int i = source.Length - m; i < source.Length; i++)
132+
{
133+
if (source[i].W == 0)
134+
{
135+
source[i] = Vector4.Zero;
136+
}
137+
}
138+
}
139+
}
140+
else if (Vector128.IsHardwareAccelerated)
141+
{
142+
for (int i = 0; i < source.Length; i++)
71143
{
72-
ref Vector4 v = ref vectorsSpan[i];
144+
ref Vector4 v = ref source[i];
73145
Vector128<float> v128 = v.AsVector128();
74146

75147
// Do `vector == 0`
@@ -80,16 +152,16 @@ private static void ClearTransparentPixelRow(
80152

81153
// Use the mask to select the replacement vector
82154
// (replacement & mask) | (v128 & ~mask)
83-
v = Vector128.ConditionalSelect(mask, replacement128, v128).AsVector4();
155+
v = Vector128.ConditionalSelect(mask, Vector128<float>.Zero, v128).AsVector4();
84156
}
85157
}
86158
else
87159
{
88-
for (int i = 0; i < vectorsSpan.Length; i++)
160+
for (int i = 0; i < source.Length; i++)
89161
{
90-
if (vectorsSpan[i].W == 0F)
162+
if (source[i].W == 0F)
91163
{
92-
vectorsSpan[i] = replacement;
164+
source[i] = Vector4.Zero;
93165
}
94166
}
95167
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.