Skip to content

Commit 7b28e25

Browse files
Merge branch 'main' into js/parallel-cleanup
2 parents 5166bed + f5238e9 commit 7b28e25

58 files changed

Lines changed: 1242 additions & 308 deletions

File tree

Some content is hidden

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

src/ImageSharp/Formats/Jpeg/Components/Block8x8F.ScaledCopy.cs

Lines changed: 324 additions & 2 deletions
Large diffs are not rendered by default.

src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Vector128.cs

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,31 +13,31 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components;
1313
internal partial struct Block8x8F
1414
{
1515
/// <summary>
16-
/// <see cref="Vector128{Single}"/> version of <see cref="NormalizeColorsInPlace(float)"/> and <see cref="RoundInPlace()"/>.
16+
/// <see cref="Vector128{Single}"/> version of <see cref="NormalizeColorsInPlace(float)"/>.
1717
/// </summary>
1818
/// <param name="maximum">The maximum value to normalize to.</param>
1919
[MethodImpl(InliningOptions.ShortMethod)]
20-
public void NormalizeColorsAndRoundInPlaceVector128(float maximum)
20+
public void NormalizeColorsInPlaceVector128(float maximum)
2121
{
2222
Vector128<float> max = Vector128.Create(maximum);
2323
Vector128<float> off = Vector128.Ceiling(max * .5F);
2424

25-
this.V0L = NormalizeAndRoundVector128(this.V0L.AsVector128(), off, max).AsVector4();
26-
this.V0R = NormalizeAndRoundVector128(this.V0R.AsVector128(), off, max).AsVector4();
27-
this.V1L = NormalizeAndRoundVector128(this.V1L.AsVector128(), off, max).AsVector4();
28-
this.V1R = NormalizeAndRoundVector128(this.V1R.AsVector128(), off, max).AsVector4();
29-
this.V2L = NormalizeAndRoundVector128(this.V2L.AsVector128(), off, max).AsVector4();
30-
this.V2R = NormalizeAndRoundVector128(this.V2R.AsVector128(), off, max).AsVector4();
31-
this.V3L = NormalizeAndRoundVector128(this.V3L.AsVector128(), off, max).AsVector4();
32-
this.V3R = NormalizeAndRoundVector128(this.V3R.AsVector128(), off, max).AsVector4();
33-
this.V4L = NormalizeAndRoundVector128(this.V4L.AsVector128(), off, max).AsVector4();
34-
this.V4R = NormalizeAndRoundVector128(this.V4R.AsVector128(), off, max).AsVector4();
35-
this.V5L = NormalizeAndRoundVector128(this.V5L.AsVector128(), off, max).AsVector4();
36-
this.V5R = NormalizeAndRoundVector128(this.V5R.AsVector128(), off, max).AsVector4();
37-
this.V6L = NormalizeAndRoundVector128(this.V6L.AsVector128(), off, max).AsVector4();
38-
this.V6R = NormalizeAndRoundVector128(this.V6R.AsVector128(), off, max).AsVector4();
39-
this.V7L = NormalizeAndRoundVector128(this.V7L.AsVector128(), off, max).AsVector4();
40-
this.V7R = NormalizeAndRoundVector128(this.V7R.AsVector128(), off, max).AsVector4();
25+
this.V0L = NormalizeVector128(this.V0L.AsVector128(), off, max).AsVector4();
26+
this.V0R = NormalizeVector128(this.V0R.AsVector128(), off, max).AsVector4();
27+
this.V1L = NormalizeVector128(this.V1L.AsVector128(), off, max).AsVector4();
28+
this.V1R = NormalizeVector128(this.V1R.AsVector128(), off, max).AsVector4();
29+
this.V2L = NormalizeVector128(this.V2L.AsVector128(), off, max).AsVector4();
30+
this.V2R = NormalizeVector128(this.V2R.AsVector128(), off, max).AsVector4();
31+
this.V3L = NormalizeVector128(this.V3L.AsVector128(), off, max).AsVector4();
32+
this.V3R = NormalizeVector128(this.V3R.AsVector128(), off, max).AsVector4();
33+
this.V4L = NormalizeVector128(this.V4L.AsVector128(), off, max).AsVector4();
34+
this.V4R = NormalizeVector128(this.V4R.AsVector128(), off, max).AsVector4();
35+
this.V5L = NormalizeVector128(this.V5L.AsVector128(), off, max).AsVector4();
36+
this.V5R = NormalizeVector128(this.V5R.AsVector128(), off, max).AsVector4();
37+
this.V6L = NormalizeVector128(this.V6L.AsVector128(), off, max).AsVector4();
38+
this.V6R = NormalizeVector128(this.V6R.AsVector128(), off, max).AsVector4();
39+
this.V7L = NormalizeVector128(this.V7L.AsVector128(), off, max).AsVector4();
40+
this.V7R = NormalizeVector128(this.V7R.AsVector128(), off, max).AsVector4();
4141
}
4242

4343
/// <summary>
@@ -71,8 +71,8 @@ public void LoadFromInt16ExtendedVector128(ref Block8x8 source)
7171
}
7272

7373
[MethodImpl(InliningOptions.ShortMethod)]
74-
private static Vector128<float> NormalizeAndRoundVector128(Vector128<float> value, Vector128<float> off, Vector128<float> max)
75-
=> Vector128_.RoundToNearestInteger(Vector128_.Clamp(value + off, Vector128<float>.Zero, max));
74+
private static Vector128<float> NormalizeVector128(Vector128<float> value, Vector128<float> off, Vector128<float> max)
75+
=> Vector128_.Clamp(value + off, Vector128<float>.Zero, max);
7676

7777
private static void MultiplyIntoInt16Vector128(ref Block8x8F a, ref Block8x8F b, ref Block8x8 dest)
7878
{

src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Vector256.cs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -39,23 +39,23 @@ internal partial struct Block8x8F
3939
#pragma warning restore SA1310 // Field names should not contain underscore
4040

4141
/// <summary>
42-
/// <see cref="Vector256{Single}"/> version of <see cref="NormalizeColorsInPlace(float)"/> and <see cref="RoundInPlace()"/>.
42+
/// <see cref="Vector256{Single}"/> version of <see cref="NormalizeColorsInPlace(float)"/>.
4343
/// </summary>
4444
/// <param name="maximum">The maximum value to normalize to.</param>
4545
[MethodImpl(InliningOptions.ShortMethod)]
46-
public void NormalizeColorsAndRoundInPlaceVector256(float maximum)
46+
public void NormalizeColorsInPlaceVector256(float maximum)
4747
{
4848
Vector256<float> max = Vector256.Create(maximum);
4949
Vector256<float> off = Vector256.Ceiling(max * .5F);
5050

51-
this.V256_0 = NormalizeAndRoundVector256(this.V256_0, off, max);
52-
this.V256_1 = NormalizeAndRoundVector256(this.V256_1, off, max);
53-
this.V256_2 = NormalizeAndRoundVector256(this.V256_2, off, max);
54-
this.V256_3 = NormalizeAndRoundVector256(this.V256_3, off, max);
55-
this.V256_4 = NormalizeAndRoundVector256(this.V256_4, off, max);
56-
this.V256_5 = NormalizeAndRoundVector256(this.V256_5, off, max);
57-
this.V256_6 = NormalizeAndRoundVector256(this.V256_6, off, max);
58-
this.V256_7 = NormalizeAndRoundVector256(this.V256_7, off, max);
51+
this.V256_0 = NormalizeVector256(this.V256_0, off, max);
52+
this.V256_1 = NormalizeVector256(this.V256_1, off, max);
53+
this.V256_2 = NormalizeVector256(this.V256_2, off, max);
54+
this.V256_3 = NormalizeVector256(this.V256_3, off, max);
55+
this.V256_4 = NormalizeVector256(this.V256_4, off, max);
56+
this.V256_5 = NormalizeVector256(this.V256_5, off, max);
57+
this.V256_6 = NormalizeVector256(this.V256_6, off, max);
58+
this.V256_7 = NormalizeVector256(this.V256_7, off, max);
5959
}
6060

6161
/// <summary>
@@ -95,10 +95,10 @@ public void LoadFromInt16ExtendedVector256(ref Block8x8 source)
9595
}
9696

9797
[MethodImpl(InliningOptions.ShortMethod)]
98-
private static Vector256<float> NormalizeAndRoundVector256(Vector256<float> value, Vector256<float> off, Vector256<float> max)
99-
=> Vector256_.RoundToNearestInteger(Vector256_.Clamp(value + off, Vector256<float>.Zero, max));
98+
private static Vector256<float> NormalizeVector256(Vector256<float> value, Vector256<float> off, Vector256<float> max)
99+
=> Vector256_.Clamp(value + off, Vector256<float>.Zero, max);
100100

101-
private static unsafe void MultiplyIntoInt16Vector256(ref Block8x8F a, ref Block8x8F b, ref Block8x8 dest)
101+
private static void MultiplyIntoInt16Vector256(ref Block8x8F a, ref Block8x8F b, ref Block8x8 dest)
102102
{
103103
DebugGuard.IsTrue(Vector256.IsHardwareAccelerated, "Vector256 support is required to run this operation!");
104104

src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs

Lines changed: 30 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ public unsafe void LoadFrom(Span<int> source)
132132
/// </summary>
133133
/// <param name="dest">Destination</param>
134134
[MethodImpl(InliningOptions.ShortMethod)]
135-
public unsafe void ScaledCopyTo(float[] dest)
135+
public readonly void ScaledCopyTo(float[] dest)
136136
{
137137
DebugGuard.MustBeGreaterThanOrEqualTo(dest.Length, Size, "dest is too small");
138138

@@ -193,7 +193,7 @@ public void MultiplyInPlace(float value)
193193
/// </summary>
194194
/// <param name="other">The other block.</param>
195195
[MethodImpl(InliningOptions.ShortMethod)]
196-
public unsafe void MultiplyInPlace(ref Block8x8F other)
196+
public void MultiplyInPlace(ref Block8x8F other)
197197
{
198198
if (Vector256.IsHardwareAccelerated)
199199
{
@@ -324,62 +324,43 @@ public Block8x8 RoundAsInt16Block()
324324
}
325325

326326
/// <summary>
327-
/// Level shift by +maximum/2, clip to [0..maximum], and round all the values in the block.
327+
/// Level shift by +maximum/2, clip to [0, maximum]
328328
/// </summary>
329-
/// <param name="maximum">The maximum value.</param>
330-
public void NormalizeColorsAndRoundInPlace(float maximum)
329+
/// <param name="maximum">The maximum value to normalize to.</param>
330+
public void NormalizeColorsInPlace(float maximum)
331331
{
332332
if (Vector256.IsHardwareAccelerated)
333333
{
334-
this.NormalizeColorsAndRoundInPlaceVector256(maximum);
334+
this.NormalizeColorsInPlaceVector256(maximum);
335+
return;
335336
}
336337
else if (Vector128.IsHardwareAccelerated)
337338
{
338-
this.NormalizeColorsAndRoundInPlaceVector128(maximum);
339+
this.NormalizeColorsInPlaceVector128(maximum);
340+
return;
339341
}
340342
else
341343
{
342-
this.NormalizeColorsInPlace(maximum);
343-
this.RoundInPlace();
344-
}
345-
}
346-
347-
/// <summary>
348-
/// Level shift by +maximum/2, clip to [0, maximum]
349-
/// </summary>
350-
/// <param name="maximum">The maximum value to normalize to.</param>
351-
public void NormalizeColorsInPlace(float maximum)
352-
{
353-
Vector4 min = Vector4.Zero;
354-
Vector4 max = new(maximum);
355-
Vector4 off = new(MathF.Ceiling(maximum * 0.5F));
356-
357-
this.V0L = Vector4.Clamp(this.V0L + off, min, max);
358-
this.V0R = Vector4.Clamp(this.V0R + off, min, max);
359-
this.V1L = Vector4.Clamp(this.V1L + off, min, max);
360-
this.V1R = Vector4.Clamp(this.V1R + off, min, max);
361-
this.V2L = Vector4.Clamp(this.V2L + off, min, max);
362-
this.V2R = Vector4.Clamp(this.V2R + off, min, max);
363-
this.V3L = Vector4.Clamp(this.V3L + off, min, max);
364-
this.V3R = Vector4.Clamp(this.V3R + off, min, max);
365-
this.V4L = Vector4.Clamp(this.V4L + off, min, max);
366-
this.V4R = Vector4.Clamp(this.V4R + off, min, max);
367-
this.V5L = Vector4.Clamp(this.V5L + off, min, max);
368-
this.V5R = Vector4.Clamp(this.V5R + off, min, max);
369-
this.V6L = Vector4.Clamp(this.V6L + off, min, max);
370-
this.V6R = Vector4.Clamp(this.V6R + off, min, max);
371-
this.V7L = Vector4.Clamp(this.V7L + off, min, max);
372-
this.V7R = Vector4.Clamp(this.V7R + off, min, max);
373-
}
374-
375-
/// <summary>
376-
/// Rounds all values in the block.
377-
/// </summary>
378-
public void RoundInPlace()
379-
{
380-
for (int i = 0; i < Size; i++)
381-
{
382-
this[i] = MathF.Round(this[i]);
344+
Vector4 min = Vector4.Zero;
345+
Vector4 max = new(maximum);
346+
Vector4 off = new(MathF.Ceiling(maximum * 0.5F));
347+
348+
this.V0L = Vector4.Clamp(this.V0L + off, min, max);
349+
this.V0R = Vector4.Clamp(this.V0R + off, min, max);
350+
this.V1L = Vector4.Clamp(this.V1L + off, min, max);
351+
this.V1R = Vector4.Clamp(this.V1R + off, min, max);
352+
this.V2L = Vector4.Clamp(this.V2L + off, min, max);
353+
this.V2R = Vector4.Clamp(this.V2R + off, min, max);
354+
this.V3L = Vector4.Clamp(this.V3L + off, min, max);
355+
this.V3R = Vector4.Clamp(this.V3R + off, min, max);
356+
this.V4L = Vector4.Clamp(this.V4L + off, min, max);
357+
this.V4R = Vector4.Clamp(this.V4R + off, min, max);
358+
this.V5L = Vector4.Clamp(this.V5L + off, min, max);
359+
this.V5R = Vector4.Clamp(this.V5R + off, min, max);
360+
this.V6L = Vector4.Clamp(this.V6L + off, min, max);
361+
this.V6R = Vector4.Clamp(this.V6R + off, min, max);
362+
this.V7L = Vector4.Clamp(this.V7L + off, min, max);
363+
this.V7R = Vector4.Clamp(this.V7R + off, min, max);
383364
}
384365
}
385366

@@ -533,7 +514,7 @@ public bool EqualsToScalar(int value)
533514
}
534515

535516
/// <inheritdoc />
536-
public bool Equals(Block8x8F other)
517+
public readonly bool Equals(Block8x8F other)
537518
=> this.V0L == other.V0L
538519
&& this.V0R == other.V0R
539520
&& this.V1L == other.V1L

src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DirectComponentProcessor.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,10 @@ public override void CopyBlocksToColorBuffer(int spectralStep)
5353
// Convert from spectral to color
5454
FloatingPointDCT.TransformIDCT(ref workspaceBlock);
5555

56-
// To conform better to libjpeg we actually NEED TO loose precision here.
57-
// This is because they store blocks as Int16 between all the operations.
58-
// To be "more accurate", we need to emulate this by rounding!
59-
workspaceBlock.NormalizeColorsAndRoundInPlace(maximumValue);
56+
// Normalize into the component sample range without quantizing away
57+
// fractional precision. The later color conversion / final pack stage
58+
// performs the only rounding we actually need for output samples.
59+
workspaceBlock.NormalizeColorsInPlace(maximumValue);
6060

6161
// Write to color buffer acording to sampling factors
6262
int xColorBufferStart = xBlock * this.BlockAreaSize.Width;

0 commit comments

Comments
 (0)