diff --git a/src/ImageSharp/Common/Tuples/Vector4Pair.cs b/src/ImageSharp/Common/Tuples/Vector4Pair.cs
index cae283d62..2e5ca3137 100644
--- a/src/ImageSharp/Common/Tuples/Vector4Pair.cs
+++ b/src/ImageSharp/Common/Tuples/Vector4Pair.cs
@@ -37,12 +37,11 @@ namespace SixLabors.ImageSharp.Tuples
this.B += other.B;
}
- ///
- /// Downscale method, specific to Jpeg color conversion. Works only if Vector{float}.Count == 4!
- /// TODO: Move it somewhere else.
+ /// .
+ /// Downscale method, specific to Jpeg color conversion. Works only if Vector{float}.Count == 4! /// TODO: Move it somewhere else.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal void RoundAndDownscalePreAvx2()
+ internal void RoundAndDownscalePreAvx2(float downscaleFactor)
{
ref Vector a = ref Unsafe.As>(ref this.A);
a = a.FastRound();
@@ -50,8 +49,8 @@ namespace SixLabors.ImageSharp.Tuples
ref Vector b = ref Unsafe.As>(ref this.B);
b = b.FastRound();
- // Downscale by 1/255
- var scale = new Vector4(1 / 255f);
+ // Downscale by 1/factor
+ var scale = new Vector4(1 / downscaleFactor);
this.A *= scale;
this.B *= scale;
}
@@ -61,14 +60,14 @@ namespace SixLabors.ImageSharp.Tuples
/// TODO: Move it somewhere else.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal void RoundAndDownscaleAvx2()
+ internal void RoundAndDownscaleAvx2(float downscaleFactor)
{
ref Vector self = ref Unsafe.As>(ref this);
Vector v = self;
v = v.FastRound();
- // Downscale by 1/255
- v *= new Vector(1 / 255f);
+ // Downscale by 1/factor
+ v *= new Vector(1 / downscaleFactor);
self = v;
}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs
index 09ed6408d..1f47de594 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs
@@ -1,6 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
+using System;
using System.Numerics;
using System.Runtime.CompilerServices;
@@ -9,10 +10,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{
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);
-
///
/// Transpose the block into the destination block.
///
@@ -94,10 +91,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
///
- /// Level shift by +128, clip to [0, 255]
+ /// Level shift by +maximum/2, clip to [0, maximum]
///
- 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.V0R = Vector4.Clamp(this.V0R + 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 and in one step.
///
[MethodImpl(InliningOptions.ShortMethod)]
- public void NormalizeColorsAndRoundInplaceAvx2()
+ public void NormalizeColorsAndRoundInplaceAvx2(float maximum)
{
- Vector off = new Vector(128f);
- Vector max = new Vector(255F);
+ Vector off = new Vector((float)Math.Ceiling(maximum/2));
+ Vector max = new Vector(maximum);
ref Vector row0 = ref Unsafe.As>(ref this.V0L);
row0 = NormalizeAndRound(row0, off, max);
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt
index f93ee6522..ec4e06e42 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt
+++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt
@@ -11,6 +11,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
+using System;
using System.Numerics;
using System.Runtime.CompilerServices;
@@ -22,10 +23,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{
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);
-
///
/// Transpose the block into the destination block.
///
@@ -59,10 +56,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
///
- /// Level shift by +128, clip to [0, 255]
+ /// Level shift by +maximum/2, clip to [0, maximum]
///
- 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(" ");
@@ -83,10 +84,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// AVX2-only variant for executing and in one step.
///
[MethodImpl(InliningOptions.ShortMethod)]
- public void NormalizeColorsAndRoundInplaceAvx2()
+ public void NormalizeColorsAndRoundInplaceAvx2(float maximum)
{
- Vector off = new Vector(128f);
- Vector max = new Vector(255F);
+ Vector off = new Vector((float)Math.Ceiling(maximum/2));
+ Vector max = new Vector(maximum);
<#
for (int i = 0; i < 8; i++)
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
index 81393342d..c9c886f05 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
@@ -467,17 +467,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
///
- /// 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.
///
- public void NormalizeColorsAndRoundInplace()
+ public void NormalizeColorsAndRoundInplace(float maximum)
{
if (SimdUtils.IsAvx2CompatibleArchitecture)
{
- this.NormalizeColorsAndRoundInplaceAvx2();
+ this.NormalizeColorsAndRoundInplaceAvx2(maximum);
}
else
{
- this.NormalizeColorsInplace();
+ this.NormalizeColorsInplace(maximum);
this.RoundInplace();
}
}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmyk.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmyk.cs
index 7a14d072e..d4dc31fe0 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmyk.cs
+++ b/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
{
- public FromCmyk()
- : base(JpegColorSpace.Cmyk)
+ public FromCmyk(int precision)
+ : 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 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++)
{
float c = cVals[i];
float m = mVals[i];
float y = yVals[i];
- float k = kVals[i] / 255F;
+ float k = kVals[i] / this.MaximumValue;
v.X = c * k;
v.Y = m * k;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs
index 7424145c3..4a5dfa632 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs
+++ b/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
{
- public FromGrayscale()
- : base(JpegColorSpace.Grayscale)
+ public FromGrayscale(int precision)
+ : base(JpegColorSpace.Grayscale, precision)
{
}
public override void ConvertToRgba(in ComponentValues values, Span 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 Vector4 dBase = ref MemoryMarshal.GetReference(result);
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgb.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgb.cs
index 7cd97c414..516dfb39f 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgb.cs
+++ b/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
{
- public FromRgb()
- : base(JpegColorSpace.RGB)
+ public FromRgb(int precision)
+ : 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 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++)
{
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs
index cb71889bc..124aac122 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs
+++ b/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
{
- public FromYCbCrBasic()
- : base(JpegColorSpace.YCbCr)
+ public FromYCbCrBasic(int precision)
+ : base(JpegColorSpace.YCbCr, precision)
{
}
public override void ConvertToRgba(in ComponentValues values, Span result)
{
- ConvertCore(values, result);
+ ConvertCore(values, result, this.MaximumValue, this.HalfValue);
}
- internal static void ConvertCore(in ComponentValues values, Span result)
+ internal static void ConvertCore(in ComponentValues values, Span result, float maxValue, float halfValue)
{
// TODO: We can optimize a lot here with Vector and SRCS.Unsafe()!
ReadOnlySpan yVals = values.Component0;
@@ -29,13 +29,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
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++)
{
float y = yVals[i];
- float cb = cbVals[i] - 128F;
- float cr = crVals[i] - 128F;
+ float cb = cbVals[i] - halfValue;
+ float cr = crVals[i] - halfValue;
v.X = MathF.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero);
v.Y = MathF.Round(y - (0.344136F * cb) - (0.714136F * cr), MidpointRounding.AwayFromZero);
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs
index 23aa1acbe..10ef02a93 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs
+++ b/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
{
- public FromYCbCrSimd()
- : base(JpegColorSpace.YCbCr)
+ public FromYCbCrSimd(int precision)
+ : base(JpegColorSpace.YCbCr, precision)
{
}
@@ -25,16 +25,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
int simdCount = result.Length - remainder;
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);
}
///
/// SIMD convert using buffers of sizes divisible by 8.
///
- internal static void ConvertCore(in ComponentValues values, Span result)
+ internal static void ConvertCore(in ComponentValues values, Span result, float maxValue, float halfValue)
{
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 Unsafe.As(ref MemoryMarshal.GetReference(result));
- var chromaOffset = new Vector4(-128f);
+ var chromaOffset = new Vector4(-halfValue);
// Walking 8 elements at one step:
int n = result.Length / 8;
@@ -58,11 +58,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
// y = yVals[i];
Vector4Pair y = Unsafe.Add(ref yBase, i);
- // cb = cbVals[i] - 128F;
+ // cb = cbVals[i] - halfValue);
Vector4Pair cb = Unsafe.Add(ref cbBase, i);
cb.AddInplace(chromaOffset);
- // cr = crVals[i] - 128F;
+ // cr = crVals[i] - halfValue;
Vector4Pair cr = Unsafe.Add(ref crBase, i);
cr.AddInplace(chromaOffset);
@@ -90,15 +90,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
if (Vector.Count == 4)
{
// TODO: Find a way to properly run & test this path on AVX2 PC-s! (Have I already mentioned that Vector is terrible?)
- r.RoundAndDownscalePreAvx2();
- g.RoundAndDownscalePreAvx2();
- b.RoundAndDownscalePreAvx2();
+ r.RoundAndDownscalePreAvx2(maxValue);
+ g.RoundAndDownscalePreAvx2(maxValue);
+ b.RoundAndDownscalePreAvx2(maxValue);
}
else if (SimdUtils.IsAvx2CompatibleArchitecture)
{
- r.RoundAndDownscaleAvx2();
- g.RoundAndDownscaleAvx2();
- b.RoundAndDownscaleAvx2();
+ r.RoundAndDownscaleAvx2(maxValue);
+ g.RoundAndDownscaleAvx2(maxValue);
+ b.RoundAndDownscaleAvx2(maxValue);
}
else
{
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs
index f0a70a6f3..9953f78c1 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs
+++ b/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
{
- public FromYCbCrSimdAvx2()
- : base(JpegColorSpace.YCbCr)
+ public FromYCbCrSimdAvx2(int precision)
+ : base(JpegColorSpace.YCbCr, precision)
{
}
@@ -28,16 +28,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
int simdCount = result.Length - remainder;
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);
}
///
/// SIMD convert using buffers of sizes divisible by 8.
///
- internal static void ConvertCore(in ComponentValues values, Span result)
+ internal static void ConvertCore(in ComponentValues values, Span result, float maxValue, float halfValue)
{
// This implementation is actually AVX specific.
// 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 Unsafe.As(ref MemoryMarshal.GetReference(result));
- var chromaOffset = new Vector(-128f);
+ var chromaOffset = new Vector(-halfValue);
// Walking 8 elements at one step:
int n = result.Length / 8;
@@ -70,7 +70,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
ref Vector ggRefAsVector = ref Unsafe.As>(ref gg);
ref Vector bbRefAsVector = ref Unsafe.As>(ref bb);
- var scale = new Vector(1 / 255f);
+ var scale = new Vector(1 / maxValue);
for (int i = 0; i < n; i++)
{
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccK.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccK.cs
index 6f940f62f..94be11e23 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccK.cs
+++ b/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
{
- public FromYccK()
- : base(JpegColorSpace.Ycck)
+ public FromYccK(int precision)
+ : 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 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++)
{
float y = yVals[i];
- float cb = cbVals[i] - 128F;
- float cr = crVals[i] - 128F;
- float k = kVals[i] / 255F;
+ float cb = cbVals[i] - this.HalfValue;
+ float cr = crVals[i] - this.HalfValue;
+ float k = kVals[i] / this.MaximumValue;
- v.X = (255F - 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.Z = (255F - MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero)) * k;
+ v.X = (this.MaximumValue - MathF.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero)) * k;
+ v.Y = (this.MaximumValue - MathF.Round(y - (0.344136F * cb) - (0.714136F * cr), MidpointRounding.AwayFromZero)) * k;
+ v.Z = (this.MaximumValue - MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero)) * k;
v.W = 1F;
v *= scale;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs
index a44ebf89d..c2e390c59 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs
@@ -3,12 +3,10 @@
using System;
using System.Collections.Generic;
-using System.Linq;
using System.Numerics;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Tuples;
-using SixLabors.Memory;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
@@ -22,15 +20,30 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
///
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),
};
///
/// Initializes a new instance of the class.
///
- protected JpegColorConverter(JpegColorSpace colorSpace)
+ protected JpegColorConverter(JpegColorSpace colorSpace, int precision)
{
this.ColorSpace = colorSpace;
+ this.Precision = precision;
+ this.MaximumValue = (float)Math.Pow(2, precision) - 1;
+ this.HalfValue = (float)Math.Ceiling(this.MaximumValue / 2);
}
///
@@ -38,12 +51,28 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
///
public JpegColorSpace ColorSpace { get; }
+ ///
+ /// Gets the Precision of this converter in bits.
+ ///
+ public int Precision { get; }
+
+ ///
+ /// Gets the maximum value of a sample
+ ///
+ private float MaximumValue { get; }
+
+ ///
+ /// Gets the half of the maximum value of a sample
+ ///
+ private float HalfValue { get; }
+
///
/// Returns the corresponding to the given
///
- 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)
{
@@ -63,8 +92,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
///
/// Returns the for the YCbCr colorspace that matches the current CPU architecture.
///
- private static JpegColorConverter GetYCbCrConverter() =>
- FromYCbCrSimdAvx2.IsAvailable ? (JpegColorConverter)new FromYCbCrSimdAvx2() : new FromYCbCrSimd();
+ private static JpegColorConverter GetYCbCrConverter(int precision) =>
+ FromYCbCrSimdAvx2.IsAvailable ? (JpegColorConverter)new FromYCbCrSimdAvx2(precision) : new FromYCbCrSimd(precision);
///
/// A stack-only struct to reference the input buffers using -s.
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs
index 1454bb5b1..ace8d7215 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs
@@ -29,10 +29,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
///
JpegColorSpace ColorSpace { get; }
+ ///
+ /// Gets the number of bits used for precision.
+ ///
+ int Precision { get; }
+
///
/// Gets the components.
///
- IEnumerable Components { get; }
+ IJpegComponent[] Components { get; }
///
/// Gets the quantization tables, in zigzag order.
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs
index da4b2847b..fe39f4188 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs
@@ -1,6 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
+using System;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory;
using SixLabors.Primitives;
@@ -38,6 +39,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
///
private Size subSamplingDivisors;
+ ///
+ /// Defines the maximum value derived from the bitdepth
+ ///
+ private int maximumValue;
+
///
/// Initializes a new instance of the struct.
///
@@ -48,6 +54,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
int qtIndex = component.QuantizationTableIndex;
this.DequantiazationTable = ZigZag.CreateDequantizationTable(ref decoder.QuantizationTables[qtIndex]);
this.subSamplingDivisors = component.SubSamplingDivisors;
+ this.maximumValue = (int)Math.Pow(2, decoder.Precision) - 1;
this.SourceBlock = 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:
/// - Dequantize
/// - 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
///
/// The source block.
/// The destination buffer area.
+ /// The maximum value derived from the bitdepth.
public void ProcessBlockColorsInto(
ref Block8x8 sourceBlock,
- in BufferArea destArea)
+ in BufferArea destArea,
+ float maximumValue)
{
ref Block8x8F b = ref this.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.
// This is because they store blocks as Int16 between all the operations.
// 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);
}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs
index 94ec600dd..e7f3e4fda 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs
@@ -78,6 +78,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
public void CopyBlocksToColorBuffer()
{
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++)
{
@@ -105,7 +106,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
this.blockAreaSize.Width,
this.blockAreaSize.Height);
- blockPp.ProcessBlockColorsInto(ref block, destArea);
+ blockPp.ProcessBlockColorsInto(ref block, destArea, maximumValue);
}
}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs
index 7ce86b4c9..438749abf 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs
@@ -3,7 +3,6 @@
using System;
using System.Buffers;
-using System.Linq;
using System.Numerics;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
@@ -57,14 +56,20 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
this.configuration = configuration;
this.RawJpeg = rawJpeg;
- IJpegComponent c0 = rawJpeg.Components.First();
+ IJpegComponent c0 = rawJpeg.Components[0];
this.NumberOfPostProcessorSteps = c0.SizeInBlocks.Height / BlockRowsPerStep;
this.PostProcessorBufferSize = new Size(c0.SizeInBlocks.Width * 8, PixelRowsPerStep);
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(rawJpeg.ImageSizeInPixels.Width);
- this.colorConverter = JpegColorConverter.GetConverter(rawJpeg.ColorSpace);
+ this.colorConverter = JpegColorConverter.GetConverter(rawJpeg.ColorSpace, rawJpeg.Precision);
}
///
@@ -152,7 +157,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
int maxY = Math.Min(destination.Height, this.PixelRowCounter + PixelRowsPerStep);
- Buffer2D[] buffers = this.ComponentProcessors.Select(cp => cp.ColorBuffer).ToArray();
+ var buffers = new Buffer2D[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++)
{
diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
index faa7c3bc9..02ae022e1 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
@@ -3,7 +3,6 @@
using System;
using System.Buffers.Binary;
-using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
@@ -33,7 +32,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
///
/// The only supported precision
///
- public const int SupportedPrecision = 8;
+ private readonly int[] supportedPrecisions = { 8, 12 };
///
/// The global configuration
@@ -137,7 +136,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
///
/// Gets the color depth, in number of bits per pixel.
///
- public int BitsPerPixel => this.ComponentCount * SupportedPrecision;
+ public int BitsPerPixel => this.ComponentCount * this.Frame.Precision;
///
/// Gets the input stream.
@@ -160,13 +159,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
///
public JpegColorSpace ColorSpace { get; private set; }
+ ///
+ public int Precision { get; private set; }
+
///
/// Gets the components.
///
public JpegComponent[] Components => this.Frame.Components;
///
- IEnumerable IRawJpegData.Components => this.Components;
+ IJpegComponent[] IRawJpegData.Components => this.Components;
///
public Block8x8F[] QuantizationTables { get; private set; }
@@ -720,12 +722,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
this.InputStream.Read(this.temp, 0, remaining);
- // We only support 8-bit precision.
- if (this.temp[0] != SupportedPrecision)
+ // We only support 8-bit and 12-bit precision.
+ 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
{
Extended = frameMarker.Marker == JpegConstants.Markers.SOF1,
diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/YCbCrColorConversion.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/YCbCrColorConversion.cs
index 05edd2791..8417b32f2 100644
--- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/YCbCrColorConversion.cs
+++ b/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);
- JpegColorConverter.FromYCbCrBasic.ConvertCore(values, this.output);
+ JpegColorConverter.FromYCbCrBasic.ConvertCore(values, this.output, 255F, 128F);
}
[Benchmark]
@@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{
var values = new JpegColorConverter.ComponentValues(this.input, 0);
- JpegColorConverter.FromYCbCrSimd.ConvertCore(values, this.output);
+ JpegColorConverter.FromYCbCrSimd.ConvertCore(values, this.output, 255F, 128F);
}
[Benchmark]
@@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{
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[] CreateRandomValues(
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs
index 81c76390c..7e7218c9d 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs
@@ -228,7 +228,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
this.PrintLinearData(input);
Block8x8F dest = block;
- dest.NormalizeColorsInplace();
+ dest.NormalizeColorsInplace(255);
float[] array = new float[64];
dest.CopyTo(array);
@@ -253,11 +253,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
Block8x8F source = CreateRandomFloatBlock(-200, 200, seed);
Block8x8F expected = source;
- expected.NormalizeColorsInplace();
+ expected.NormalizeColorsInplace(255);
expected.RoundInplace();
Block8x8F actual = source;
- actual.NormalizeColorsAndRoundInplaceAvx2();
+ actual.NormalizeColorsAndRoundInplaceAvx2(255);
this.Output.WriteLine(expected.ToString());
this.Output.WriteLine(actual.ToString());
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs
index 8e30eb9e5..caaad73c9 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs
+++ b/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)
{
ValidateRgbToYCbCrConversion(
- new JpegColorConverter.FromYCbCrBasic(),
+ new JpegColorConverter.FromYCbCrBasic(8),
3,
inputBufferLength,
resultBufferLength,
@@ -75,7 +75,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
JpegColorConverter.ComponentValues values = CreateRandomValues(3, size, seed);
var result = new Vector4[size];
- JpegColorConverter.FromYCbCrSimd.ConvertCore(values, result);
+ JpegColorConverter.FromYCbCrSimd.ConvertCore(values, result, 255, 128);
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)
{
ValidateRgbToYCbCrConversion(
- new JpegColorConverter.FromYCbCrSimd(),
+ new JpegColorConverter.FromYCbCrSimd(8),
3,
inputBufferLength,
resultBufferLength,
@@ -108,7 +108,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
//JpegColorConverter.FromYCbCrSimdAvx2.LogPlz = s => this.Output.WriteLine(s);
ValidateRgbToYCbCrConversion(
- new JpegColorConverter.FromYCbCrSimdAvx2(),
+ new JpegColorConverter.FromYCbCrSimdAvx2(8),
3,
inputBufferLength,
resultBufferLength,
@@ -140,7 +140,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
JpegColorConverter.ComponentValues values = CreateRandomValues(3, count, 1);
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:
converter.ConvertToRgba(values, result);
@@ -161,7 +161,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
var v = new Vector4(0, 0, 0, 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);
var result = new Vector4[resultBufferLength];
@@ -194,7 +194,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[MemberData(nameof(CommonConversionData))]
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);
var result = new Vector4[resultBufferLength];
@@ -216,7 +216,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[MemberData(nameof(CommonConversionData))]
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);
var result = new Vector4[resultBufferLength];
@@ -243,7 +243,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
var v = new Vector4(0, 0, 0, 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);
var result = new Vector4[resultBufferLength];
@@ -308,7 +308,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
int seed)
{
ValidateRgbToYCbCrConversion(
- JpegColorConverter.GetConverter(colorSpace),
+ JpegColorConverter.GetConverter(colorSpace,8),
componentCount,
inputBufferLength,
resultBufferLength,
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs
index 40de25b30..03f1826ed 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs
+++ b/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.InvalidAPP0721,
TestImages.Jpeg.Issues.ExifGetString750Load,
- TestImages.Jpeg.Issues.ExifGetString750Transform
+ TestImages.Jpeg.Issues.ExifGetString750Transform,
+
+ // High depth images
+ TestImages.Jpeg.Baseline.Testorig12bit,
};
public static string[] ProgressiveTestJpegs =
diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj
index 7452d6e49..e20f1514c 100644
--- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj
+++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj
@@ -27,7 +27,7 @@
-
+
diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs
index e5b93ab77..6141e9e2a 100644
--- a/tests/ImageSharp.Tests/TestImages.cs
+++ b/tests/ImageSharp.Tests/TestImages.cs
@@ -135,12 +135,14 @@ namespace SixLabors.ImageSharp.Tests
public const string Testorig420 = "Jpg/baseline/testorig.jpg";
public const string MultiScanBaselineCMYK = "Jpg/baseline/MultiScanBaselineCMYK.jpg";
public const string Ratio1x1 = "Jpg/baseline/ratio-1x1.jpg";
+ public const string Testorig12bit = "Jpg/baseline/testorig12.jpg";
public static readonly string[] All =
{
Cmyk, Ycck, Exif, Floorplan,
Calliphora, Turtle, GammaDalaiLamaGray,
- Hiyamugi, Jpeg400, Jpeg420Exif, Jpeg444, Ratio1x1
+ Hiyamugi, Jpeg400, Jpeg420Exif, Jpeg444,
+ Ratio1x1, Testorig12bit
};
}
diff --git a/tests/Images/External b/tests/Images/External
index 69603ee5b..7ada45bc3 160000
--- a/tests/Images/External
+++ b/tests/Images/External
@@ -1 +1 @@
-Subproject commit 69603ee5b6f7dd64114fc44d321e50d9b2d439be
+Subproject commit 7ada45bc3484f40e28a50817386ca93f293acd11
diff --git a/tests/Images/Input/Jpg/baseline/testorig12.jpg b/tests/Images/Input/Jpg/baseline/testorig12.jpg
new file mode 100644
index 000000000..5cc7a0ada
--- /dev/null
+++ b/tests/Images/Input/Jpg/baseline/testorig12.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:34790770f76db8e60a7765b52ca4edf5f16bc21bcb8c6045ca2efef39a8a013e
+size 12394