Browse Source

Merge pull request #784 from feliwir/jpeg-12bit

Jpeg 12 bit support
af/merge-core
James Jackson-South 7 years ago
committed by GitHub
parent
commit
b9638be032
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 17
      src/ImageSharp/Common/Tuples/Vector4Pair.cs
  2. 19
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs
  3. 19
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt
  4. 8
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
  5. 12
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmyk.cs
  6. 10
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs
  7. 10
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgb.cs
  8. 14
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs
  9. 28
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs
  10. 14
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs
  11. 22
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccK.cs
  12. 45
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs
  13. 7
      src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs
  14. 15
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs
  15. 3
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs
  16. 19
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs
  17. 18
      src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
  18. 6
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/YCbCrColorConversion.cs
  19. 6
      tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs
  20. 20
      tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs
  21. 5
      tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs
  22. 2
      tests/ImageSharp.Tests/ImageSharp.Tests.csproj
  23. 4
      tests/ImageSharp.Tests/TestImages.cs
  24. 2
      tests/Images/External
  25. 3
      tests/Images/Input/Jpg/baseline/testorig12.jpg

17
src/ImageSharp/Common/Tuples/Vector4Pair.cs

@ -37,12 +37,11 @@ namespace SixLabors.ImageSharp.Tuples
this.B += other.B; this.B += other.B;
} }
/// <summary> /// <summary>.
/// Downscale method, specific to Jpeg color conversion. Works only if Vector{float}.Count == 4! /// Downscale method, specific to Jpeg color conversion. Works only if Vector{float}.Count == 4! /// TODO: Move it somewhere else.
/// TODO: Move it somewhere else.
/// </summary> /// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void RoundAndDownscalePreAvx2() internal void RoundAndDownscalePreAvx2(float downscaleFactor)
{ {
ref Vector<float> a = ref Unsafe.As<Vector4, Vector<float>>(ref this.A); ref Vector<float> a = ref Unsafe.As<Vector4, Vector<float>>(ref this.A);
a = a.FastRound(); a = a.FastRound();
@ -50,8 +49,8 @@ namespace SixLabors.ImageSharp.Tuples
ref Vector<float> b = ref Unsafe.As<Vector4, Vector<float>>(ref this.B); ref Vector<float> b = ref Unsafe.As<Vector4, Vector<float>>(ref this.B);
b = b.FastRound(); b = b.FastRound();
// Downscale by 1/255 // Downscale by 1/factor
var scale = new Vector4(1 / 255f); var scale = new Vector4(1 / downscaleFactor);
this.A *= scale; this.A *= scale;
this.B *= scale; this.B *= scale;
} }
@ -61,14 +60,14 @@ namespace SixLabors.ImageSharp.Tuples
/// TODO: Move it somewhere else. /// TODO: Move it somewhere else.
/// </summary> /// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void RoundAndDownscaleAvx2() internal void RoundAndDownscaleAvx2(float downscaleFactor)
{ {
ref Vector<float> self = ref Unsafe.As<Vector4Pair, Vector<float>>(ref this); ref Vector<float> self = ref Unsafe.As<Vector4Pair, Vector<float>>(ref this);
Vector<float> v = self; Vector<float> v = self;
v = v.FastRound(); v = v.FastRound();
// Downscale by 1/255 // Downscale by 1/factor
v *= new Vector<float>(1 / 255f); v *= new Vector<float>(1 / downscaleFactor);
self = v; self = v;
} }

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

@ -1,6 +1,7 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -9,10 +10,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{ {
internal partial struct Block8x8F internal partial struct Block8x8F
{ {
private static readonly Vector4 CMin4 = new Vector4(0F);
private static readonly Vector4 CMax4 = new Vector4(255F);
private static readonly Vector4 COff4 = new Vector4(128F);
/// <summary> /// <summary>
/// Transpose the block into the destination block. /// Transpose the block into the destination block.
/// </summary> /// </summary>
@ -94,10 +91,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
} }
/// <summary> /// <summary>
/// Level shift by +128, clip to [0, 255] /// Level shift by +maximum/2, clip to [0, maximum]
/// </summary> /// </summary>
public void NormalizeColorsInplace() public void NormalizeColorsInplace(float maximum)
{ {
Vector4 CMin4 = new Vector4(0F);
Vector4 CMax4 = new Vector4(maximum);
Vector4 COff4 = new Vector4((float)Math.Ceiling(maximum/2));
this.V0L = Vector4.Clamp(this.V0L + COff4, CMin4, CMax4); this.V0L = Vector4.Clamp(this.V0L + COff4, CMin4, CMax4);
this.V0R = Vector4.Clamp(this.V0R + COff4, CMin4, CMax4); this.V0R = Vector4.Clamp(this.V0R + COff4, CMin4, CMax4);
this.V1L = Vector4.Clamp(this.V1L + COff4, CMin4, CMax4); this.V1L = Vector4.Clamp(this.V1L + COff4, CMin4, CMax4);
@ -120,10 +121,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// AVX2-only variant for executing <see cref="NormalizeColorsInplace"/> and <see cref="RoundInplace"/> in one step. /// AVX2-only variant for executing <see cref="NormalizeColorsInplace"/> and <see cref="RoundInplace"/> in one step.
/// </summary> /// </summary>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void NormalizeColorsAndRoundInplaceAvx2() public void NormalizeColorsAndRoundInplaceAvx2(float maximum)
{ {
Vector<float> off = new Vector<float>(128f); Vector<float> off = new Vector<float>((float)Math.Ceiling(maximum/2));
Vector<float> max = new Vector<float>(255F); Vector<float> max = new Vector<float>(maximum);
ref Vector<float> row0 = ref Unsafe.As<Vector4, Vector<float>>(ref this.V0L); ref Vector<float> row0 = ref Unsafe.As<Vector4, Vector<float>>(ref this.V0L);
row0 = NormalizeAndRound(row0, off, max); row0 = NormalizeAndRound(row0, off, max);

19
src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt

@ -11,6 +11,7 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -22,10 +23,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{ {
internal partial struct Block8x8F internal partial struct Block8x8F
{ {
private static readonly Vector4 CMin4 = new Vector4(0F);
private static readonly Vector4 CMax4 = new Vector4(255F);
private static readonly Vector4 COff4 = new Vector4(128F);
/// <summary> /// <summary>
/// Transpose the block into the destination block. /// Transpose the block into the destination block.
/// </summary> /// </summary>
@ -59,10 +56,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
} }
/// <summary> /// <summary>
/// Level shift by +128, clip to [0, 255] /// Level shift by +maximum/2, clip to [0, maximum]
/// </summary> /// </summary>
public void NormalizeColorsInplace() public void NormalizeColorsInplace(float maximum)
{ {
Vector4 CMin4 = new Vector4(0F);
Vector4 CMax4 = new Vector4(maximum);
Vector4 COff4 = new Vector4((float)Math.Ceiling(maximum/2));
<# <#
PushIndent(" "); PushIndent(" ");
@ -83,10 +84,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// AVX2-only variant for executing <see cref="NormalizeColorsInplace"/> and <see cref="RoundInplace"/> in one step. /// AVX2-only variant for executing <see cref="NormalizeColorsInplace"/> and <see cref="RoundInplace"/> in one step.
/// </summary> /// </summary>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void NormalizeColorsAndRoundInplaceAvx2() public void NormalizeColorsAndRoundInplaceAvx2(float maximum)
{ {
Vector<float> off = new Vector<float>(128f); Vector<float> off = new Vector<float>((float)Math.Ceiling(maximum/2));
Vector<float> max = new Vector<float>(255F); Vector<float> max = new Vector<float>(maximum);
<# <#
for (int i = 0; i < 8; i++) for (int i = 0; i < 8; i++)

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

@ -467,17 +467,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
} }
/// <summary> /// <summary>
/// Level shift by +128, clip to [0..255], and round all the values in the block. /// Level shift by +maximum/2, clip to [0..maximum], and round all the values in the block.
/// </summary> /// </summary>
public void NormalizeColorsAndRoundInplace() public void NormalizeColorsAndRoundInplace(float maximum)
{ {
if (SimdUtils.IsAvx2CompatibleArchitecture) if (SimdUtils.IsAvx2CompatibleArchitecture)
{ {
this.NormalizeColorsAndRoundInplaceAvx2(); this.NormalizeColorsAndRoundInplaceAvx2(maximum);
} }
else else
{ {
this.NormalizeColorsInplace(); this.NormalizeColorsInplace(maximum);
this.RoundInplace(); this.RoundInplace();
} }
} }

12
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmyk.cs

@ -10,8 +10,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{ {
internal class FromCmyk : JpegColorConverter internal class FromCmyk : JpegColorConverter
{ {
public FromCmyk() public FromCmyk(int precision)
: base(JpegColorSpace.Cmyk) : base(JpegColorSpace.Cmyk, precision)
{ {
} }
@ -25,14 +25,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
var v = new Vector4(0, 0, 0, 1F); var v = new Vector4(0, 0, 0, 1F);
var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F); var scale = new Vector4(
1 / this.MaximumValue,
1 / this.MaximumValue,
1 / this.MaximumValue,
1F);
for (int i = 0; i < result.Length; i++) for (int i = 0; i < result.Length; i++)
{ {
float c = cVals[i]; float c = cVals[i];
float m = mVals[i]; float m = mVals[i];
float y = yVals[i]; float y = yVals[i];
float k = kVals[i] / 255F; float k = kVals[i] / this.MaximumValue;
v.X = c * k; v.X = c * k;
v.Y = m * k; v.Y = m * k;

10
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs

@ -12,14 +12,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{ {
internal class FromGrayscale : JpegColorConverter internal class FromGrayscale : JpegColorConverter
{ {
public FromGrayscale() public FromGrayscale(int precision)
: base(JpegColorSpace.Grayscale) : base(JpegColorSpace.Grayscale, precision)
{ {
} }
public override void ConvertToRgba(in ComponentValues values, Span<Vector4> result) public override void ConvertToRgba(in ComponentValues values, Span<Vector4> result)
{ {
var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F); var scale = new Vector4(
1 / this.MaximumValue,
1 / this.MaximumValue,
1 / this.MaximumValue,
1F);
ref float sBase = ref MemoryMarshal.GetReference(values.Component0); ref float sBase = ref MemoryMarshal.GetReference(values.Component0);
ref Vector4 dBase = ref MemoryMarshal.GetReference(result); ref Vector4 dBase = ref MemoryMarshal.GetReference(result);

10
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgb.cs

@ -10,8 +10,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{ {
internal class FromRgb : JpegColorConverter internal class FromRgb : JpegColorConverter
{ {
public FromRgb() public FromRgb(int precision)
: base(JpegColorSpace.RGB) : base(JpegColorSpace.RGB, precision)
{ {
} }
@ -24,7 +24,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
var v = new Vector4(0, 0, 0, 1); var v = new Vector4(0, 0, 0, 1);
var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F); var scale = new Vector4(
1 / this.MaximumValue,
1 / this.MaximumValue,
1 / this.MaximumValue,
1F);
for (int i = 0; i < result.Length; i++) for (int i = 0; i < result.Length; i++)
{ {

14
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs

@ -10,17 +10,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{ {
internal class FromYCbCrBasic : JpegColorConverter internal class FromYCbCrBasic : JpegColorConverter
{ {
public FromYCbCrBasic() public FromYCbCrBasic(int precision)
: base(JpegColorSpace.YCbCr) : base(JpegColorSpace.YCbCr, precision)
{ {
} }
public override void ConvertToRgba(in ComponentValues values, Span<Vector4> result) public override void ConvertToRgba(in ComponentValues values, Span<Vector4> result)
{ {
ConvertCore(values, result); ConvertCore(values, result, this.MaximumValue, this.HalfValue);
} }
internal static void ConvertCore(in ComponentValues values, Span<Vector4> result) internal static void ConvertCore(in ComponentValues values, Span<Vector4> result, float maxValue, float halfValue)
{ {
// TODO: We can optimize a lot here with Vector<float> and SRCS.Unsafe()! // TODO: We can optimize a lot here with Vector<float> and SRCS.Unsafe()!
ReadOnlySpan<float> yVals = values.Component0; ReadOnlySpan<float> yVals = values.Component0;
@ -29,13 +29,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
var v = new Vector4(0, 0, 0, 1); var v = new Vector4(0, 0, 0, 1);
var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F); var scale = new Vector4(1 / maxValue, 1 / maxValue, 1 / maxValue, 1F);
for (int i = 0; i < result.Length; i++) for (int i = 0; i < result.Length; i++)
{ {
float y = yVals[i]; float y = yVals[i];
float cb = cbVals[i] - 128F; float cb = cbVals[i] - halfValue;
float cr = crVals[i] - 128F; float cr = crVals[i] - halfValue;
v.X = MathF.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero); v.X = MathF.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero);
v.Y = MathF.Round(y - (0.344136F * cb) - (0.714136F * cr), MidpointRounding.AwayFromZero); v.Y = MathF.Round(y - (0.344136F * cb) - (0.714136F * cr), MidpointRounding.AwayFromZero);

28
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs

@ -14,8 +14,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{ {
internal class FromYCbCrSimd : JpegColorConverter internal class FromYCbCrSimd : JpegColorConverter
{ {
public FromYCbCrSimd() public FromYCbCrSimd(int precision)
: base(JpegColorSpace.YCbCr) : base(JpegColorSpace.YCbCr, precision)
{ {
} }
@ -25,16 +25,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
int simdCount = result.Length - remainder; int simdCount = result.Length - remainder;
if (simdCount > 0) if (simdCount > 0)
{ {
ConvertCore(values.Slice(0, simdCount), result.Slice(0, simdCount)); ConvertCore(values.Slice(0, simdCount), result.Slice(0, simdCount), this.MaximumValue, this.HalfValue);
} }
FromYCbCrBasic.ConvertCore(values.Slice(simdCount, remainder), result.Slice(simdCount, remainder)); FromYCbCrBasic.ConvertCore(values.Slice(simdCount, remainder), result.Slice(simdCount, remainder), this.MaximumValue, this.HalfValue);
} }
/// <summary> /// <summary>
/// SIMD convert using buffers of sizes divisible by 8. /// SIMD convert using buffers of sizes divisible by 8.
/// </summary> /// </summary>
internal static void ConvertCore(in ComponentValues values, Span<Vector4> result) internal static void ConvertCore(in ComponentValues values, Span<Vector4> result, float maxValue, float halfValue)
{ {
DebugGuard.IsTrue(result.Length % 8 == 0, nameof(result), "result.Length should be divisible by 8!"); DebugGuard.IsTrue(result.Length % 8 == 0, nameof(result), "result.Length should be divisible by 8!");
@ -48,7 +48,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
ref Vector4Octet resultBase = ref Vector4Octet resultBase =
ref Unsafe.As<Vector4, Vector4Octet>(ref MemoryMarshal.GetReference(result)); ref Unsafe.As<Vector4, Vector4Octet>(ref MemoryMarshal.GetReference(result));
var chromaOffset = new Vector4(-128f); var chromaOffset = new Vector4(-halfValue);
// Walking 8 elements at one step: // Walking 8 elements at one step:
int n = result.Length / 8; int n = result.Length / 8;
@ -58,11 +58,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
// y = yVals[i]; // y = yVals[i];
Vector4Pair y = Unsafe.Add(ref yBase, i); Vector4Pair y = Unsafe.Add(ref yBase, i);
// cb = cbVals[i] - 128F; // cb = cbVals[i] - halfValue);
Vector4Pair cb = Unsafe.Add(ref cbBase, i); Vector4Pair cb = Unsafe.Add(ref cbBase, i);
cb.AddInplace(chromaOffset); cb.AddInplace(chromaOffset);
// cr = crVals[i] - 128F; // cr = crVals[i] - halfValue;
Vector4Pair cr = Unsafe.Add(ref crBase, i); Vector4Pair cr = Unsafe.Add(ref crBase, i);
cr.AddInplace(chromaOffset); cr.AddInplace(chromaOffset);
@ -90,15 +90,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
if (Vector<float>.Count == 4) if (Vector<float>.Count == 4)
{ {
// TODO: Find a way to properly run & test this path on AVX2 PC-s! (Have I already mentioned that Vector<T> is terrible?) // TODO: Find a way to properly run & test this path on AVX2 PC-s! (Have I already mentioned that Vector<T> is terrible?)
r.RoundAndDownscalePreAvx2(); r.RoundAndDownscalePreAvx2(maxValue);
g.RoundAndDownscalePreAvx2(); g.RoundAndDownscalePreAvx2(maxValue);
b.RoundAndDownscalePreAvx2(); b.RoundAndDownscalePreAvx2(maxValue);
} }
else if (SimdUtils.IsAvx2CompatibleArchitecture) else if (SimdUtils.IsAvx2CompatibleArchitecture)
{ {
r.RoundAndDownscaleAvx2(); r.RoundAndDownscaleAvx2(maxValue);
g.RoundAndDownscaleAvx2(); g.RoundAndDownscaleAvx2(maxValue);
b.RoundAndDownscaleAvx2(); b.RoundAndDownscaleAvx2(maxValue);
} }
else else
{ {

14
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs

@ -15,8 +15,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{ {
internal class FromYCbCrSimdAvx2 : JpegColorConverter internal class FromYCbCrSimdAvx2 : JpegColorConverter
{ {
public FromYCbCrSimdAvx2() public FromYCbCrSimdAvx2(int precision)
: base(JpegColorSpace.YCbCr) : base(JpegColorSpace.YCbCr, precision)
{ {
} }
@ -28,16 +28,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
int simdCount = result.Length - remainder; int simdCount = result.Length - remainder;
if (simdCount > 0) if (simdCount > 0)
{ {
ConvertCore(values.Slice(0, simdCount), result.Slice(0, simdCount)); ConvertCore(values.Slice(0, simdCount), result.Slice(0, simdCount), this.MaximumValue, this.HalfValue);
} }
FromYCbCrBasic.ConvertCore(values.Slice(simdCount, remainder), result.Slice(simdCount, remainder)); FromYCbCrBasic.ConvertCore(values.Slice(simdCount, remainder), result.Slice(simdCount, remainder), this.MaximumValue, this.HalfValue);
} }
/// <summary> /// <summary>
/// SIMD convert using buffers of sizes divisible by 8. /// SIMD convert using buffers of sizes divisible by 8.
/// </summary> /// </summary>
internal static void ConvertCore(in ComponentValues values, Span<Vector4> result) internal static void ConvertCore(in ComponentValues values, Span<Vector4> result, float maxValue, float halfValue)
{ {
// This implementation is actually AVX specific. // This implementation is actually AVX specific.
// An AVX register is capable of storing 8 float-s. // An AVX register is capable of storing 8 float-s.
@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
ref Vector4Octet resultBase = ref Vector4Octet resultBase =
ref Unsafe.As<Vector4, Vector4Octet>(ref MemoryMarshal.GetReference(result)); ref Unsafe.As<Vector4, Vector4Octet>(ref MemoryMarshal.GetReference(result));
var chromaOffset = new Vector<float>(-128f); var chromaOffset = new Vector<float>(-halfValue);
// Walking 8 elements at one step: // Walking 8 elements at one step:
int n = result.Length / 8; int n = result.Length / 8;
@ -70,7 +70,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
ref Vector<float> ggRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref gg); ref Vector<float> ggRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref gg);
ref Vector<float> bbRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref bb); ref Vector<float> bbRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref bb);
var scale = new Vector<float>(1 / 255f); var scale = new Vector<float>(1 / maxValue);
for (int i = 0; i < n; i++) for (int i = 0; i < n; i++)
{ {

22
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccK.cs

@ -10,8 +10,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{ {
internal class FromYccK : JpegColorConverter internal class FromYccK : JpegColorConverter
{ {
public FromYccK() public FromYccK(int precision)
: base(JpegColorSpace.Ycck) : base(JpegColorSpace.Ycck, precision)
{ {
} }
@ -25,18 +25,22 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
var v = new Vector4(0, 0, 0, 1F); var v = new Vector4(0, 0, 0, 1F);
var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F); var scale = new Vector4(
1 / this.MaximumValue,
1 / this.MaximumValue,
1 / this.MaximumValue,
1F);
for (int i = 0; i < result.Length; i++) for (int i = 0; i < result.Length; i++)
{ {
float y = yVals[i]; float y = yVals[i];
float cb = cbVals[i] - 128F; float cb = cbVals[i] - this.HalfValue;
float cr = crVals[i] - 128F; float cr = crVals[i] - this.HalfValue;
float k = kVals[i] / 255F; float k = kVals[i] / this.MaximumValue;
v.X = (255F - MathF.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero)) * k; v.X = (this.MaximumValue - MathF.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero)) * k;
v.Y = (255F - MathF.Round(y - (0.344136F * cb) - (0.714136F * cr), MidpointRounding.AwayFromZero)) * k; v.Y = (this.MaximumValue - MathF.Round(y - (0.344136F * cb) - (0.714136F * cr), MidpointRounding.AwayFromZero)) * k;
v.Z = (255F - MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero)) * k; v.Z = (this.MaximumValue - MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero)) * k;
v.W = 1F; v.W = 1F;
v *= scale; v *= scale;

45
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs

@ -3,12 +3,10 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Numerics; using System.Numerics;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Tuples; using SixLabors.ImageSharp.Tuples;
using SixLabors.Memory;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{ {
@ -22,15 +20,30 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
/// </summary> /// </summary>
private static readonly JpegColorConverter[] Converters = private static readonly JpegColorConverter[] Converters =
{ {
GetYCbCrConverter(), new FromYccK(), new FromCmyk(), new FromGrayscale(), new FromRgb() // 8-bit converters
GetYCbCrConverter(8),
new FromYccK(8),
new FromCmyk(8),
new FromGrayscale(8),
new FromRgb(8),
// 12-bit converters
GetYCbCrConverter(12),
new FromYccK(12),
new FromCmyk(12),
new FromGrayscale(12),
new FromRgb(12),
}; };
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="JpegColorConverter"/> class. /// Initializes a new instance of the <see cref="JpegColorConverter"/> class.
/// </summary> /// </summary>
protected JpegColorConverter(JpegColorSpace colorSpace) protected JpegColorConverter(JpegColorSpace colorSpace, int precision)
{ {
this.ColorSpace = colorSpace; this.ColorSpace = colorSpace;
this.Precision = precision;
this.MaximumValue = (float)Math.Pow(2, precision) - 1;
this.HalfValue = (float)Math.Ceiling(this.MaximumValue / 2);
} }
/// <summary> /// <summary>
@ -38,12 +51,28 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
/// </summary> /// </summary>
public JpegColorSpace ColorSpace { get; } public JpegColorSpace ColorSpace { get; }
/// <summary>
/// Gets the Precision of this converter in bits.
/// </summary>
public int Precision { get; }
/// <summary>
/// Gets the maximum value of a sample
/// </summary>
private float MaximumValue { get; }
/// <summary>
/// Gets the half of the maximum value of a sample
/// </summary>
private float HalfValue { get; }
/// <summary> /// <summary>
/// Returns the <see cref="JpegColorConverter"/> corresponding to the given <see cref="JpegColorSpace"/> /// Returns the <see cref="JpegColorConverter"/> corresponding to the given <see cref="JpegColorSpace"/>
/// </summary> /// </summary>
public static JpegColorConverter GetConverter(JpegColorSpace colorSpace) public static JpegColorConverter GetConverter(JpegColorSpace colorSpace, float precision)
{ {
JpegColorConverter converter = Converters.FirstOrDefault(c => c.ColorSpace == colorSpace); JpegColorConverter converter = Array.Find(Converters, c => c.ColorSpace == colorSpace
&& c.Precision == precision);
if (converter is null) if (converter is null)
{ {
@ -63,8 +92,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
/// <summary> /// <summary>
/// Returns the <see cref="JpegColorConverter"/> for the YCbCr colorspace that matches the current CPU architecture. /// Returns the <see cref="JpegColorConverter"/> for the YCbCr colorspace that matches the current CPU architecture.
/// </summary> /// </summary>
private static JpegColorConverter GetYCbCrConverter() => private static JpegColorConverter GetYCbCrConverter(int precision) =>
FromYCbCrSimdAvx2.IsAvailable ? (JpegColorConverter)new FromYCbCrSimdAvx2() : new FromYCbCrSimd(); FromYCbCrSimdAvx2.IsAvailable ? (JpegColorConverter)new FromYCbCrSimdAvx2(precision) : new FromYCbCrSimd(precision);
/// <summary> /// <summary>
/// A stack-only struct to reference the input buffers using <see cref="ReadOnlySpan{T}"/>-s. /// A stack-only struct to reference the input buffers using <see cref="ReadOnlySpan{T}"/>-s.

7
src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs

@ -29,10 +29,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// </summary> /// </summary>
JpegColorSpace ColorSpace { get; } JpegColorSpace ColorSpace { get; }
/// <summary>
/// Gets the number of bits used for precision.
/// </summary>
int Precision { get; }
/// <summary> /// <summary>
/// Gets the components. /// Gets the components.
/// </summary> /// </summary>
IEnumerable<IJpegComponent> Components { get; } IJpegComponent[] Components { get; }
/// <summary> /// <summary>
/// Gets the quantization tables, in zigzag order. /// Gets the quantization tables, in zigzag order.

15
src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs

@ -1,6 +1,7 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.Primitives; using SixLabors.Primitives;
@ -38,6 +39,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// </summary> /// </summary>
private Size subSamplingDivisors; private Size subSamplingDivisors;
/// <summary>
/// Defines the maximum value derived from the bitdepth
/// </summary>
private int maximumValue;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="JpegBlockPostProcessor"/> struct. /// Initializes a new instance of the <see cref="JpegBlockPostProcessor"/> struct.
/// </summary> /// </summary>
@ -48,6 +54,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
int qtIndex = component.QuantizationTableIndex; int qtIndex = component.QuantizationTableIndex;
this.DequantiazationTable = ZigZag.CreateDequantizationTable(ref decoder.QuantizationTables[qtIndex]); this.DequantiazationTable = ZigZag.CreateDequantizationTable(ref decoder.QuantizationTables[qtIndex]);
this.subSamplingDivisors = component.SubSamplingDivisors; this.subSamplingDivisors = component.SubSamplingDivisors;
this.maximumValue = (int)Math.Pow(2, decoder.Precision) - 1;
this.SourceBlock = default; this.SourceBlock = default;
this.WorkspaceBlock1 = default; this.WorkspaceBlock1 = default;
@ -58,14 +65,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// Processes 'sourceBlock' producing Jpeg color channel values from spectral values: /// Processes 'sourceBlock' producing Jpeg color channel values from spectral values:
/// - Dequantize /// - Dequantize
/// - Applying IDCT /// - Applying IDCT
/// - Level shift by +128, clip to [0, 255] /// - Level shift by +maximumValue/2, clip to [0, maximumValue]
/// - Copy the resulting color values into 'destArea' scaling up the block by amount defined in <see cref="subSamplingDivisors"/> /// - Copy the resulting color values into 'destArea' scaling up the block by amount defined in <see cref="subSamplingDivisors"/>
/// </summary> /// </summary>
/// <param name="sourceBlock">The source block.</param> /// <param name="sourceBlock">The source block.</param>
/// <param name="destArea">The destination buffer area.</param> /// <param name="destArea">The destination buffer area.</param>
/// <param name="maximumValue">The maximum value derived from the bitdepth.</param>
public void ProcessBlockColorsInto( public void ProcessBlockColorsInto(
ref Block8x8 sourceBlock, ref Block8x8 sourceBlock,
in BufferArea<float> destArea) in BufferArea<float> destArea,
float maximumValue)
{ {
ref Block8x8F b = ref this.SourceBlock; ref Block8x8F b = ref this.SourceBlock;
b.LoadFrom(ref sourceBlock); b.LoadFrom(ref sourceBlock);
@ -78,7 +87,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
// To conform better to libjpeg we actually NEED TO loose precision here. // To conform better to libjpeg we actually NEED TO loose precision here.
// This is because they store blocks as Int16 between all the operations. // This is because they store blocks as Int16 between all the operations.
// To be "more accurate", we need to emulate this by rounding! // To be "more accurate", we need to emulate this by rounding!
this.WorkspaceBlock1.NormalizeColorsAndRoundInplace(); this.WorkspaceBlock1.NormalizeColorsAndRoundInplace(maximumValue);
this.WorkspaceBlock1.CopyTo(destArea, this.subSamplingDivisors.Width, this.subSamplingDivisors.Height); this.WorkspaceBlock1.CopyTo(destArea, this.subSamplingDivisors.Width, this.subSamplingDivisors.Height);
} }

3
src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs

@ -78,6 +78,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
public void CopyBlocksToColorBuffer() public void CopyBlocksToColorBuffer()
{ {
var blockPp = new JpegBlockPostProcessor(this.ImagePostProcessor.RawJpeg, this.Component); var blockPp = new JpegBlockPostProcessor(this.ImagePostProcessor.RawJpeg, this.Component);
float maximumValue = (float)Math.Pow(2, this.ImagePostProcessor.RawJpeg.Precision) - 1;
for (int y = 0; y < this.BlockRowsPerStep; y++) for (int y = 0; y < this.BlockRowsPerStep; y++)
{ {
@ -105,7 +106,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
this.blockAreaSize.Width, this.blockAreaSize.Width,
this.blockAreaSize.Height); this.blockAreaSize.Height);
blockPp.ProcessBlockColorsInto(ref block, destArea); blockPp.ProcessBlockColorsInto(ref block, destArea, maximumValue);
} }
} }

19
src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs

@ -3,7 +3,6 @@
using System; using System;
using System.Buffers; using System.Buffers;
using System.Linq;
using System.Numerics; using System.Numerics;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
@ -57,14 +56,20 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{ {
this.configuration = configuration; this.configuration = configuration;
this.RawJpeg = rawJpeg; this.RawJpeg = rawJpeg;
IJpegComponent c0 = rawJpeg.Components.First(); IJpegComponent c0 = rawJpeg.Components[0];
this.NumberOfPostProcessorSteps = c0.SizeInBlocks.Height / BlockRowsPerStep; this.NumberOfPostProcessorSteps = c0.SizeInBlocks.Height / BlockRowsPerStep;
this.PostProcessorBufferSize = new Size(c0.SizeInBlocks.Width * 8, PixelRowsPerStep); this.PostProcessorBufferSize = new Size(c0.SizeInBlocks.Width * 8, PixelRowsPerStep);
MemoryAllocator memoryAllocator = configuration.MemoryAllocator; MemoryAllocator memoryAllocator = configuration.MemoryAllocator;
this.ComponentProcessors = rawJpeg.Components.Select(c => new JpegComponentPostProcessor(memoryAllocator, this, c)).ToArray();
this.ComponentProcessors = new JpegComponentPostProcessor[rawJpeg.Components.Length];
for (int i = 0; i < rawJpeg.Components.Length; i++)
{
this.ComponentProcessors[i] = new JpegComponentPostProcessor(memoryAllocator, this, rawJpeg.Components[i]);
}
this.rgbaBuffer = memoryAllocator.Allocate<Vector4>(rawJpeg.ImageSizeInPixels.Width); this.rgbaBuffer = memoryAllocator.Allocate<Vector4>(rawJpeg.ImageSizeInPixels.Width);
this.colorConverter = JpegColorConverter.GetConverter(rawJpeg.ColorSpace); this.colorConverter = JpegColorConverter.GetConverter(rawJpeg.ColorSpace, rawJpeg.Precision);
} }
/// <summary> /// <summary>
@ -152,7 +157,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{ {
int maxY = Math.Min(destination.Height, this.PixelRowCounter + PixelRowsPerStep); int maxY = Math.Min(destination.Height, this.PixelRowCounter + PixelRowsPerStep);
Buffer2D<float>[] buffers = this.ComponentProcessors.Select(cp => cp.ColorBuffer).ToArray(); var buffers = new Buffer2D<float>[this.ComponentProcessors.Length];
for (int i = 0; i < this.ComponentProcessors.Length; i++)
{
buffers[i] = this.ComponentProcessors[i].ColorBuffer;
}
for (int yy = this.PixelRowCounter; yy < maxY; yy++) for (int yy = this.PixelRowCounter; yy < maxY; yy++)
{ {

18
src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs

@ -3,7 +3,6 @@
using System; using System;
using System.Buffers.Binary; using System.Buffers.Binary;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -33,7 +32,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <summary> /// <summary>
/// The only supported precision /// The only supported precision
/// </summary> /// </summary>
public const int SupportedPrecision = 8; private readonly int[] supportedPrecisions = { 8, 12 };
/// <summary> /// <summary>
/// The global configuration /// The global configuration
@ -137,7 +136,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <summary> /// <summary>
/// Gets the color depth, in number of bits per pixel. /// Gets the color depth, in number of bits per pixel.
/// </summary> /// </summary>
public int BitsPerPixel => this.ComponentCount * SupportedPrecision; public int BitsPerPixel => this.ComponentCount * this.Frame.Precision;
/// <summary> /// <summary>
/// Gets the input stream. /// Gets the input stream.
@ -160,13 +159,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <inheritdoc/> /// <inheritdoc/>
public JpegColorSpace ColorSpace { get; private set; } public JpegColorSpace ColorSpace { get; private set; }
/// <inheritdoc/>
public int Precision { get; private set; }
/// <summary> /// <summary>
/// Gets the components. /// Gets the components.
/// </summary> /// </summary>
public JpegComponent[] Components => this.Frame.Components; public JpegComponent[] Components => this.Frame.Components;
/// <inheritdoc/> /// <inheritdoc/>
IEnumerable<IJpegComponent> IRawJpegData.Components => this.Components; IJpegComponent[] IRawJpegData.Components => this.Components;
/// <inheritdoc/> /// <inheritdoc/>
public Block8x8F[] QuantizationTables { get; private set; } public Block8x8F[] QuantizationTables { get; private set; }
@ -720,12 +722,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
this.InputStream.Read(this.temp, 0, remaining); this.InputStream.Read(this.temp, 0, remaining);
// We only support 8-bit precision. // We only support 8-bit and 12-bit precision.
if (this.temp[0] != SupportedPrecision) if (!this.supportedPrecisions.Contains(this.temp[0]))
{ {
throw new ImageFormatException("Only 8-Bit precision supported."); throw new ImageFormatException("Only 8-Bit and 12-Bit precision supported.");
} }
this.Precision = this.temp[0];
this.Frame = new JpegFrame this.Frame = new JpegFrame
{ {
Extended = frameMarker.Marker == JpegConstants.Markers.SOF1, Extended = frameMarker.Marker == JpegConstants.Markers.SOF1,

6
tests/ImageSharp.Benchmarks/Codecs/Jpeg/YCbCrColorConversion.cs

@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{ {
var values = new JpegColorConverter.ComponentValues(this.input, 0); var values = new JpegColorConverter.ComponentValues(this.input, 0);
JpegColorConverter.FromYCbCrBasic.ConvertCore(values, this.output); JpegColorConverter.FromYCbCrBasic.ConvertCore(values, this.output, 255F, 128F);
} }
[Benchmark] [Benchmark]
@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{ {
var values = new JpegColorConverter.ComponentValues(this.input, 0); var values = new JpegColorConverter.ComponentValues(this.input, 0);
JpegColorConverter.FromYCbCrSimd.ConvertCore(values, this.output); JpegColorConverter.FromYCbCrSimd.ConvertCore(values, this.output, 255F, 128F);
} }
[Benchmark] [Benchmark]
@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{ {
var values = new JpegColorConverter.ComponentValues(this.input, 0); var values = new JpegColorConverter.ComponentValues(this.input, 0);
JpegColorConverter.FromYCbCrSimdAvx2.ConvertCore(values, this.output); JpegColorConverter.FromYCbCrSimdAvx2.ConvertCore(values, this.output, 255F, 128F);
} }
private static Buffer2D<float>[] CreateRandomValues( private static Buffer2D<float>[] CreateRandomValues(

6
tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs

@ -228,7 +228,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
this.PrintLinearData(input); this.PrintLinearData(input);
Block8x8F dest = block; Block8x8F dest = block;
dest.NormalizeColorsInplace(); dest.NormalizeColorsInplace(255);
float[] array = new float[64]; float[] array = new float[64];
dest.CopyTo(array); dest.CopyTo(array);
@ -253,11 +253,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
Block8x8F source = CreateRandomFloatBlock(-200, 200, seed); Block8x8F source = CreateRandomFloatBlock(-200, 200, seed);
Block8x8F expected = source; Block8x8F expected = source;
expected.NormalizeColorsInplace(); expected.NormalizeColorsInplace(255);
expected.RoundInplace(); expected.RoundInplace();
Block8x8F actual = source; Block8x8F actual = source;
actual.NormalizeColorsAndRoundInplaceAvx2(); actual.NormalizeColorsAndRoundInplaceAvx2(255);
this.Output.WriteLine(expected.ToString()); this.Output.WriteLine(expected.ToString());
this.Output.WriteLine(actual.ToString()); this.Output.WriteLine(actual.ToString());

20
tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs

@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
public void ConvertFromYCbCrBasic(int inputBufferLength, int resultBufferLength, int seed) public void ConvertFromYCbCrBasic(int inputBufferLength, int resultBufferLength, int seed)
{ {
ValidateRgbToYCbCrConversion( ValidateRgbToYCbCrConversion(
new JpegColorConverter.FromYCbCrBasic(), new JpegColorConverter.FromYCbCrBasic(8),
3, 3,
inputBufferLength, inputBufferLength,
resultBufferLength, resultBufferLength,
@ -75,7 +75,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
JpegColorConverter.ComponentValues values = CreateRandomValues(3, size, seed); JpegColorConverter.ComponentValues values = CreateRandomValues(3, size, seed);
var result = new Vector4[size]; var result = new Vector4[size];
JpegColorConverter.FromYCbCrSimd.ConvertCore(values, result); JpegColorConverter.FromYCbCrSimd.ConvertCore(values, result, 255, 128);
for (int i = 0; i < size; i++) for (int i = 0; i < size; i++)
{ {
@ -88,7 +88,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
public void FromYCbCrSimd(int inputBufferLength, int resultBufferLength, int seed) public void FromYCbCrSimd(int inputBufferLength, int resultBufferLength, int seed)
{ {
ValidateRgbToYCbCrConversion( ValidateRgbToYCbCrConversion(
new JpegColorConverter.FromYCbCrSimd(), new JpegColorConverter.FromYCbCrSimd(8),
3, 3,
inputBufferLength, inputBufferLength,
resultBufferLength, resultBufferLength,
@ -108,7 +108,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
//JpegColorConverter.FromYCbCrSimdAvx2.LogPlz = s => this.Output.WriteLine(s); //JpegColorConverter.FromYCbCrSimdAvx2.LogPlz = s => this.Output.WriteLine(s);
ValidateRgbToYCbCrConversion( ValidateRgbToYCbCrConversion(
new JpegColorConverter.FromYCbCrSimdAvx2(), new JpegColorConverter.FromYCbCrSimdAvx2(8),
3, 3,
inputBufferLength, inputBufferLength,
resultBufferLength, resultBufferLength,
@ -140,7 +140,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
JpegColorConverter.ComponentValues values = CreateRandomValues(3, count, 1); JpegColorConverter.ComponentValues values = CreateRandomValues(3, count, 1);
var result = new Vector4[count]; var result = new Vector4[count];
JpegColorConverter converter = simd ? (JpegColorConverter)new JpegColorConverter.FromYCbCrSimd() : new JpegColorConverter.FromYCbCrBasic(); JpegColorConverter converter = simd ? (JpegColorConverter)new JpegColorConverter.FromYCbCrSimd(8) : new JpegColorConverter.FromYCbCrBasic(8);
// Warm up: // Warm up:
converter.ConvertToRgba(values, result); converter.ConvertToRgba(values, result);
@ -161,7 +161,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
var v = new Vector4(0, 0, 0, 1F); var v = new Vector4(0, 0, 0, 1F);
var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F); var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F);
var converter = JpegColorConverter.GetConverter(JpegColorSpace.Cmyk); var converter = JpegColorConverter.GetConverter(JpegColorSpace.Cmyk, 8);
JpegColorConverter.ComponentValues values = CreateRandomValues(4, inputBufferLength, seed); JpegColorConverter.ComponentValues values = CreateRandomValues(4, inputBufferLength, seed);
var result = new Vector4[resultBufferLength]; var result = new Vector4[resultBufferLength];
@ -194,7 +194,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[MemberData(nameof(CommonConversionData))] [MemberData(nameof(CommonConversionData))]
public void ConvertFromGrayScale(int inputBufferLength, int resultBufferLength, int seed) public void ConvertFromGrayScale(int inputBufferLength, int resultBufferLength, int seed)
{ {
var converter = JpegColorConverter.GetConverter(JpegColorSpace.Grayscale); var converter = JpegColorConverter.GetConverter(JpegColorSpace.Grayscale, 8);
JpegColorConverter.ComponentValues values = CreateRandomValues(1, inputBufferLength, seed); JpegColorConverter.ComponentValues values = CreateRandomValues(1, inputBufferLength, seed);
var result = new Vector4[resultBufferLength]; var result = new Vector4[resultBufferLength];
@ -216,7 +216,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[MemberData(nameof(CommonConversionData))] [MemberData(nameof(CommonConversionData))]
public void ConvertFromRgb(int inputBufferLength, int resultBufferLength, int seed) public void ConvertFromRgb(int inputBufferLength, int resultBufferLength, int seed)
{ {
var converter = JpegColorConverter.GetConverter(JpegColorSpace.RGB); var converter = JpegColorConverter.GetConverter(JpegColorSpace.RGB, 8);
JpegColorConverter.ComponentValues values = CreateRandomValues(3, inputBufferLength, seed); JpegColorConverter.ComponentValues values = CreateRandomValues(3, inputBufferLength, seed);
var result = new Vector4[resultBufferLength]; var result = new Vector4[resultBufferLength];
@ -243,7 +243,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
var v = new Vector4(0, 0, 0, 1F); var v = new Vector4(0, 0, 0, 1F);
var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F); var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F);
var converter = JpegColorConverter.GetConverter(JpegColorSpace.Ycck); var converter = JpegColorConverter.GetConverter(JpegColorSpace.Ycck, 8);
JpegColorConverter.ComponentValues values = CreateRandomValues(4, inputBufferLength, seed); JpegColorConverter.ComponentValues values = CreateRandomValues(4, inputBufferLength, seed);
var result = new Vector4[resultBufferLength]; var result = new Vector4[resultBufferLength];
@ -308,7 +308,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
int seed) int seed)
{ {
ValidateRgbToYCbCrConversion( ValidateRgbToYCbCrConversion(
JpegColorConverter.GetConverter(colorSpace), JpegColorConverter.GetConverter(colorSpace,8),
componentCount, componentCount,
inputBufferLength, inputBufferLength,
resultBufferLength, resultBufferLength,

5
tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs

@ -28,7 +28,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
TestImages.Jpeg.Issues.ExifResizeOutOfRange696, TestImages.Jpeg.Issues.ExifResizeOutOfRange696,
TestImages.Jpeg.Issues.InvalidAPP0721, TestImages.Jpeg.Issues.InvalidAPP0721,
TestImages.Jpeg.Issues.ExifGetString750Load, TestImages.Jpeg.Issues.ExifGetString750Load,
TestImages.Jpeg.Issues.ExifGetString750Transform TestImages.Jpeg.Issues.ExifGetString750Transform,
// High depth images
TestImages.Jpeg.Baseline.Testorig12bit,
}; };
public static string[] ProgressiveTestJpegs = public static string[] ProgressiveTestJpegs =

2
tests/ImageSharp.Tests/ImageSharp.Tests.csproj

@ -27,7 +27,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Magick.NET-Q16-AnyCPU" Version="7.9.0.1" /> <PackageReference Include="Magick.NET-Q16-AnyCPU" Version="7.9.2" />
<PackageReference Include="System.Drawing.Common" Version="4.5.0" /> <PackageReference Include="System.Drawing.Common" Version="4.5.0" />
<PackageReference Include="xunit" Version="2.3.1" /> <PackageReference Include="xunit" Version="2.3.1" />
<PackageReference Include="xunit.runner.console" Version="2.3.1" /> <PackageReference Include="xunit.runner.console" Version="2.3.1" />

4
tests/ImageSharp.Tests/TestImages.cs

@ -135,12 +135,14 @@ namespace SixLabors.ImageSharp.Tests
public const string Testorig420 = "Jpg/baseline/testorig.jpg"; public const string Testorig420 = "Jpg/baseline/testorig.jpg";
public const string MultiScanBaselineCMYK = "Jpg/baseline/MultiScanBaselineCMYK.jpg"; public const string MultiScanBaselineCMYK = "Jpg/baseline/MultiScanBaselineCMYK.jpg";
public const string Ratio1x1 = "Jpg/baseline/ratio-1x1.jpg"; public const string Ratio1x1 = "Jpg/baseline/ratio-1x1.jpg";
public const string Testorig12bit = "Jpg/baseline/testorig12.jpg";
public static readonly string[] All = public static readonly string[] All =
{ {
Cmyk, Ycck, Exif, Floorplan, Cmyk, Ycck, Exif, Floorplan,
Calliphora, Turtle, GammaDalaiLamaGray, Calliphora, Turtle, GammaDalaiLamaGray,
Hiyamugi, Jpeg400, Jpeg420Exif, Jpeg444, Ratio1x1 Hiyamugi, Jpeg400, Jpeg420Exif, Jpeg444,
Ratio1x1, Testorig12bit
}; };
} }

2
tests/Images/External

@ -1 +1 @@
Subproject commit 69603ee5b6f7dd64114fc44d321e50d9b2d439be Subproject commit 7ada45bc3484f40e28a50817386ca93f293acd11

3
tests/Images/Input/Jpg/baseline/testorig12.jpg

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:34790770f76db8e60a7765b52ca4edf5f16bc21bcb8c6045ca2efef39a8a013e
size 12394
Loading…
Cancel
Save