Skip to content

Commit 1c6e1d9

Browse files
authored
Merge branch 'main' into bp/Issue2679
2 parents 5c60126 + 8da27c9 commit 1c6e1d9

File tree

689 files changed

+16225
-18203
lines changed

Some content is hidden

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

689 files changed

+16225
-18203
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424
sdk: 8.0.x
2525
runtime: -x64
2626
codecov: false
27-
- os: macos-latest
27+
- os: macos-13 # macos-latest runs on arm64 runners where libgdiplus is unavailable
2828
framework: net8.0
2929
sdk: 8.0.x
3030
runtime: -x64
@@ -133,7 +133,7 @@ jobs:
133133
XUNIT_PATH: .\tests\ImageSharp.Tests # Required for xunit
134134

135135
- name: Export Failed Output
136-
uses: actions/upload-artifact@v3
136+
uses: actions/upload-artifact@v4
137137
if: failure()
138138
with:
139139
name: actual_output_${{ runner.os }}_${{ matrix.options.framework }}${{ matrix.options.runtime }}.zip

.github/workflows/code-coverage.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ jobs:
7474
XUNIT_PATH: .\tests\ImageSharp.Tests # Required for xunit
7575

7676
- name: Export Failed Output
77-
uses: actions/upload-artifact@v3
77+
uses: actions/upload-artifact@v4
7878
if: failure()
7979
with:
8080
name: actual_output_${{ runner.os }}_${{ matrix.options.framework }}${{ matrix.options.runtime }}.zip

ImageSharp.sln

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "issues", "issues", "{5C9B68
238238
tests\Images\Input\Jpg\issues\issue750-exif-tranform.jpg = tests\Images\Input\Jpg\issues\issue750-exif-tranform.jpg
239239
tests\Images\Input\Jpg\issues\Issue845-Incorrect-Quality99.jpg = tests\Images\Input\Jpg\issues\Issue845-Incorrect-Quality99.jpg
240240
tests\Images\Input\Jpg\issues\issue855-incorrect-colorspace.jpg = tests\Images\Input\Jpg\issues\issue855-incorrect-colorspace.jpg
241+
tests\Images\Input\Jpg\issues\issue-2067-comment.jpg = tests\Images\Input\Jpg\issues\issue-2067-comment.jpg
241242
EndProjectSection
242243
EndProject
243244
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "fuzz", "fuzz", "{516A3532-6AC2-417B-AD79-9BD5D0D378A0}"
@@ -660,6 +661,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Qoi", "Qoi", "{E801B508-493
660661
tests\Images\Input\Qoi\wikipedia_008.qoi = tests\Images\Input\Qoi\wikipedia_008.qoi
661662
EndProjectSection
662663
EndProject
664+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Icon", "Icon", "{95E45DDE-A67D-48AD-BBA8-5FAA151B860D}"
665+
ProjectSection(SolutionItems) = preProject
666+
tests\Images\Input\Icon\aero_arrow.cur = tests\Images\Input\Icon\aero_arrow.cur
667+
tests\Images\Input\Icon\flutter.ico = tests\Images\Input\Icon\flutter.ico
668+
EndProjectSection
669+
EndProject
663670
Global
664671
GlobalSection(SolutionConfigurationPlatforms) = preSolution
665672
Debug|Any CPU = Debug|Any CPU
@@ -713,6 +720,7 @@ Global
713720
{670DD46C-82E9-499A-B2D2-00A802ED0141} = {E1C42A6F-913B-4A7B-B1A8-2BB62843B254}
714721
{5DFC394F-136F-4B76-9BCA-3BA786515EFC} = {9DA226A1-8656-49A8-A58A-A8B5C081AD66}
715722
{E801B508-4935-41CD-BA85-CF11BFF55A45} = {9DA226A1-8656-49A8-A58A-A8B5C081AD66}
723+
{95E45DDE-A67D-48AD-BBA8-5FAA151B860D} = {9DA226A1-8656-49A8-A58A-A8B5C081AD66}
716724
EndGlobalSection
717725
GlobalSection(ExtensibilityGlobals) = postSolution
718726
SolutionGuid = {5F8B9D1F-CD8B-4CC5-8216-D531E25BD795}

src/ImageSharp/Advanced/AotCompilerTools.cs

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,13 @@
1010
using SixLabors.ImageSharp.Formats.Gif;
1111
using SixLabors.ImageSharp.Formats.Jpeg;
1212
using SixLabors.ImageSharp.Formats.Jpeg.Components;
13+
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder;
1314
using SixLabors.ImageSharp.Formats.Pbm;
1415
using SixLabors.ImageSharp.Formats.Png;
16+
using SixLabors.ImageSharp.Formats.Qoi;
1517
using SixLabors.ImageSharp.Formats.Tga;
1618
using SixLabors.ImageSharp.Formats.Tiff;
19+
using SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors;
1720
using SixLabors.ImageSharp.Formats.Webp;
1821
using SixLabors.ImageSharp.Memory;
1922
using SixLabors.ImageSharp.PixelFormats;
@@ -129,6 +132,7 @@ private static void Seed<TPixel>()
129132
AotCompileImageDecoderInternals<TPixel>();
130133
AotCompileImageEncoders<TPixel>();
131134
AotCompileImageDecoders<TPixel>();
135+
AotCompileSpectralConverter<TPixel>();
132136
AotCompileImageProcessors<TPixel>();
133137
AotCompileGenericImageProcessors<TPixel>();
134138
AotCompileResamplers<TPixel>();
@@ -195,39 +199,41 @@ private static void AotCompileImageProcessingContextFactory<TPixel>()
195199
=> default(DefaultImageOperationsProviderFactory).CreateImageProcessingContext<TPixel>(default, default, default);
196200

197201
/// <summary>
198-
/// This method pre-seeds the all <see cref="IImageEncoderInternals"/> in the AoT compiler.
202+
/// This method pre-seeds the all core encoders in the AoT compiler.
199203
/// </summary>
200204
/// <typeparam name="TPixel">The pixel format.</typeparam>
201205
[Preserve]
202206
private static void AotCompileImageEncoderInternals<TPixel>()
203207
where TPixel : unmanaged, IPixel<TPixel>
204208
{
205-
default(WebpEncoderCore).Encode<TPixel>(default, default, default);
206209
default(BmpEncoderCore).Encode<TPixel>(default, default, default);
207210
default(GifEncoderCore).Encode<TPixel>(default, default, default);
208211
default(JpegEncoderCore).Encode<TPixel>(default, default, default);
209212
default(PbmEncoderCore).Encode<TPixel>(default, default, default);
210213
default(PngEncoderCore).Encode<TPixel>(default, default, default);
214+
default(QoiEncoderCore).Encode<TPixel>(default, default, default);
211215
default(TgaEncoderCore).Encode<TPixel>(default, default, default);
212216
default(TiffEncoderCore).Encode<TPixel>(default, default, default);
217+
default(WebpEncoderCore).Encode<TPixel>(default, default, default);
213218
}
214219

215220
/// <summary>
216-
/// This method pre-seeds the all <see cref="IImageDecoderInternals"/> in the AoT compiler.
221+
/// This method pre-seeds the all <see cref="ImageDecoderCore"/> in the AoT compiler.
217222
/// </summary>
218223
/// <typeparam name="TPixel">The pixel format.</typeparam>
219224
[Preserve]
220225
private static void AotCompileImageDecoderInternals<TPixel>()
221226
where TPixel : unmanaged, IPixel<TPixel>
222227
{
223-
default(WebpDecoderCore).Decode<TPixel>(default, default);
224-
default(BmpDecoderCore).Decode<TPixel>(default, default);
225-
default(GifDecoderCore).Decode<TPixel>(default, default);
226-
default(JpegDecoderCore).Decode<TPixel>(default, default);
227-
default(PbmDecoderCore).Decode<TPixel>(default, default);
228-
default(PngDecoderCore).Decode<TPixel>(default, default);
229-
default(TgaDecoderCore).Decode<TPixel>(default, default);
230-
default(TiffDecoderCore).Decode<TPixel>(default, default);
228+
default(BmpDecoderCore).Decode<TPixel>(default, default, default);
229+
default(GifDecoderCore).Decode<TPixel>(default, default, default);
230+
default(JpegDecoderCore).Decode<TPixel>(default, default, default);
231+
default(PbmDecoderCore).Decode<TPixel>(default, default, default);
232+
default(PngDecoderCore).Decode<TPixel>(default, default, default);
233+
default(QoiDecoderCore).Decode<TPixel>(default, default, default);
234+
default(TgaDecoderCore).Decode<TPixel>(default, default, default);
235+
default(TiffDecoderCore).Decode<TPixel>(default, default, default);
236+
default(WebpDecoderCore).Decode<TPixel>(default, default, default);
231237
}
232238

233239
/// <summary>
@@ -266,6 +272,17 @@ private static void AotCompileImageDecoders<TPixel>()
266272
AotCompileImageDecoder<TPixel, TiffDecoder>();
267273
}
268274

275+
[Preserve]
276+
private static void AotCompileSpectralConverter<TPixel>()
277+
where TPixel : unmanaged, IPixel<TPixel>
278+
{
279+
default(SpectralConverter<TPixel>).GetPixelBuffer(default);
280+
default(GrayJpegSpectralConverter<TPixel>).GetPixelBuffer(default);
281+
default(RgbJpegSpectralConverter<TPixel>).GetPixelBuffer(default);
282+
default(TiffJpegSpectralConverter<TPixel>).GetPixelBuffer(default);
283+
default(TiffOldJpegSpectralConverter<TPixel>).GetPixelBuffer(default);
284+
}
285+
269286
/// <summary>
270287
/// This method pre-seeds the <see cref="IImageEncoder"/> in the AoT compiler.
271288
/// </summary>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Six Labors Split License.
3+
4+
namespace SixLabors.ImageSharp.ColorProfiles;
5+
6+
/// <summary>
7+
/// Enumerate the possible sources of the white point used in chromatic adaptation.
8+
/// </summary>
9+
public enum ChromaticAdaptionWhitePointSource
10+
{
11+
/// <summary>
12+
/// The white point of the source color space.
13+
/// </summary>
14+
WhitePoint,
15+
16+
/// <summary>
17+
/// The white point of the source working space.
18+
/// </summary>
19+
RgbWorkingSpace
20+
}
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
// Copyright (c) Six Labors.
1+
// Copyright (c) Six Labors.
22
// Licensed under the Six Labors Split License.
33

4-
namespace SixLabors.ImageSharp.ColorSpaces.Conversion;
4+
namespace SixLabors.ImageSharp.ColorProfiles;
55

66
/// <summary>
77
/// Constants use for Cie conversion calculations
@@ -12,10 +12,10 @@ internal static class CieConstants
1212
/// <summary>
1313
/// 216F / 24389F
1414
/// </summary>
15-
public const float Epsilon = 0.008856452F;
15+
public const float Epsilon = 216f / 24389f;
1616

1717
/// <summary>
1818
/// 24389F / 27F
1919
/// </summary>
20-
public const float Kappa = 903.2963F;
20+
public const float Kappa = 24389f / 27f;
2121
}
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Six Labors Split License.
3+
4+
using System.Numerics;
5+
using System.Runtime.CompilerServices;
6+
using System.Runtime.InteropServices;
7+
8+
namespace SixLabors.ImageSharp.ColorProfiles;
9+
10+
/// <summary>
11+
/// Represents a CIE L*a*b* 1976 color.
12+
/// <see href="https://en.wikipedia.org/wiki/Lab_color_space"/>
13+
/// </summary>
14+
[StructLayout(LayoutKind.Sequential)]
15+
public readonly struct CieLab : IProfileConnectingSpace<CieLab, CieXyz>
16+
{
17+
/// <summary>
18+
/// Initializes a new instance of the <see cref="CieLab"/> struct.
19+
/// </summary>
20+
/// <param name="l">The lightness dimension.</param>
21+
/// <param name="a">The a (green - magenta) component.</param>
22+
/// <param name="b">The b (blue - yellow) component.</param>
23+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
24+
public CieLab(float l, float a, float b)
25+
{
26+
// Not clamping as documentation about this space only indicates "usual" ranges
27+
this.L = l;
28+
this.A = a;
29+
this.B = b;
30+
}
31+
32+
/// <summary>
33+
/// Initializes a new instance of the <see cref="CieLab"/> struct.
34+
/// </summary>
35+
/// <param name="vector">The vector representing the l, a, b components.</param>
36+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
37+
public CieLab(Vector3 vector)
38+
: this()
39+
{
40+
this.L = vector.X;
41+
this.A = vector.Y;
42+
this.B = vector.Z;
43+
}
44+
45+
/// <summary>
46+
/// Gets the lightness dimension.
47+
/// <remarks>A value usually ranging between 0 (black), 100 (diffuse white) or higher (specular white).</remarks>
48+
/// </summary>
49+
public float L { get; }
50+
51+
/// <summary>
52+
/// Gets the a color component.
53+
/// <remarks>A value usually ranging from -100 to 100. Negative is green, positive magenta.</remarks>
54+
/// </summary>
55+
public float A { get; }
56+
57+
/// <summary>
58+
/// Gets the b color component.
59+
/// <remarks>A value usually ranging from -100 to 100. Negative is blue, positive is yellow</remarks>
60+
/// </summary>
61+
public float B { get; }
62+
63+
/// <summary>
64+
/// Compares two <see cref="CieLab"/> objects for equality.
65+
/// </summary>
66+
/// <param name="left">The <see cref="CieLab"/> on the left side of the operand.</param>
67+
/// <param name="right">The <see cref="CieLab"/> on the right side of the operand.</param>
68+
/// <returns>
69+
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
70+
/// </returns>
71+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
72+
public static bool operator ==(CieLab left, CieLab right) => left.Equals(right);
73+
74+
/// <summary>
75+
/// Compares two <see cref="CieLab"/> objects for inequality
76+
/// </summary>
77+
/// <param name="left">The <see cref="CieLab"/> on the left side of the operand.</param>
78+
/// <param name="right">The <see cref="CieLab"/> on the right side of the operand.</param>
79+
/// <returns>
80+
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
81+
/// </returns>
82+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
83+
public static bool operator !=(CieLab left, CieLab right) => !left.Equals(right);
84+
85+
/// <inheritdoc/>
86+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
87+
public static CieLab FromProfileConnectingSpace(ColorConversionOptions options, in CieXyz source)
88+
{
89+
// Conversion algorithm described here:
90+
// http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Lab.html
91+
CieXyz whitePoint = options.TargetWhitePoint;
92+
float wx = whitePoint.X, wy = whitePoint.Y, wz = whitePoint.Z;
93+
94+
float xr = source.X / wx, yr = source.Y / wy, zr = source.Z / wz;
95+
96+
const float inv116 = 1 / 116F;
97+
98+
float fx = xr > CieConstants.Epsilon ? MathF.Pow(xr, 0.3333333F) : ((CieConstants.Kappa * xr) + 16F) * inv116;
99+
float fy = yr > CieConstants.Epsilon ? MathF.Pow(yr, 0.3333333F) : ((CieConstants.Kappa * yr) + 16F) * inv116;
100+
float fz = zr > CieConstants.Epsilon ? MathF.Pow(zr, 0.3333333F) : ((CieConstants.Kappa * zr) + 16F) * inv116;
101+
102+
float l = (116F * fy) - 16F;
103+
float a = 500F * (fx - fy);
104+
float b = 200F * (fy - fz);
105+
106+
return new CieLab(l, a, b);
107+
}
108+
109+
/// <inheritdoc/>
110+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
111+
public static void FromProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan<CieXyz> source, Span<CieLab> destination)
112+
{
113+
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
114+
115+
for (int i = 0; i < source.Length; i++)
116+
{
117+
CieXyz xyz = source[i];
118+
destination[i] = FromProfileConnectingSpace(options, in xyz);
119+
}
120+
}
121+
122+
/// <inheritdoc/>
123+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
124+
public CieXyz ToProfileConnectingSpace(ColorConversionOptions options)
125+
{
126+
// Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_Lab_to_XYZ.html
127+
float l = this.L, a = this.A, b = this.B;
128+
float fy = (l + 16) / 116F;
129+
float fx = (a / 500F) + fy;
130+
float fz = fy - (b / 200F);
131+
132+
float fx3 = Numerics.Pow3(fx);
133+
float fz3 = Numerics.Pow3(fz);
134+
135+
float xr = fx3 > CieConstants.Epsilon ? fx3 : ((116F * fx) - 16F) / CieConstants.Kappa;
136+
float yr = l > CieConstants.Kappa * CieConstants.Epsilon ? Numerics.Pow3((l + 16F) / 116F) : l / CieConstants.Kappa;
137+
float zr = fz3 > CieConstants.Epsilon ? fz3 : ((116F * fz) - 16F) / CieConstants.Kappa;
138+
139+
CieXyz whitePoint = options.WhitePoint;
140+
Vector3 wxyz = new(whitePoint.X, whitePoint.Y, whitePoint.Z);
141+
Vector3 xyzr = new(xr, yr, zr);
142+
143+
return new(xyzr * wxyz);
144+
}
145+
146+
/// <inheritdoc/>
147+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
148+
public static void ToProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan<CieLab> source, Span<CieXyz> destination)
149+
{
150+
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
151+
152+
for (int i = 0; i < source.Length; i++)
153+
{
154+
CieLab lab = source[i];
155+
destination[i] = lab.ToProfileConnectingSpace(options);
156+
}
157+
}
158+
159+
/// <inheritdoc/>
160+
public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource()
161+
=> ChromaticAdaptionWhitePointSource.WhitePoint;
162+
163+
/// <inheritdoc/>
164+
public override int GetHashCode() => HashCode.Combine(this.L, this.A, this.B);
165+
166+
/// <inheritdoc/>
167+
public override string ToString() => FormattableString.Invariant($"CieLab({this.L:#0.##}, {this.A:#0.##}, {this.B:#0.##})");
168+
169+
/// <inheritdoc/>
170+
public override bool Equals(object? obj) => obj is CieLab other && this.Equals(other);
171+
172+
/// <inheritdoc/>
173+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
174+
public bool Equals(CieLab other)
175+
=> this.AsVector3Unsafe() == other.AsVector3Unsafe();
176+
177+
private Vector3 AsVector3Unsafe() => Unsafe.As<CieLab, Vector3>(ref Unsafe.AsRef(in this));
178+
}

0 commit comments

Comments
 (0)