diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs index e5494c7dc5..3d2a91a4fd 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs @@ -968,10 +968,10 @@ namespace SixLabors.ImageSharp } internal static void UnpackToRgbPlanesAvx2Reduce( - ref ReadOnlySpan redChannel, - ref ReadOnlySpan greenChannel, - ref ReadOnlySpan blueChannel, - ref Span source) + ref Span redChannel, + ref Span greenChannel, + ref Span blueChannel, + ref ReadOnlySpan source) { ref Vector256 rgbByteSpan = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref Vector256 destRRef = ref Unsafe.As>(ref MemoryMarshal.GetReference(redChannel)); diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.Pack.cs b/src/ImageSharp/Common/Helpers/SimdUtils.Pack.cs index b45c8c7d6f..b8c14698af 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.Pack.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.Pack.cs @@ -67,10 +67,10 @@ namespace SixLabors.ImageSharp [MethodImpl(InliningOptions.ShortMethod)] internal static void UnpackToRgbPlanes( - ReadOnlySpan redChannel, - ReadOnlySpan greenChannel, - ReadOnlySpan blueChannel, - Span source) + Span redChannel, + Span greenChannel, + Span blueChannel, + ReadOnlySpan source) { DebugGuard.IsTrue(greenChannel.Length == redChannel.Length, nameof(greenChannel), "Channels must be of same size!"); DebugGuard.IsTrue(blueChannel.Length == redChannel.Length, nameof(blueChannel), "Channels must be of same size!"); @@ -221,11 +221,15 @@ namespace SixLabors.ImageSharp } private static void UnpackToRgbPlanesScalar( - ReadOnlySpan redChannel, - ReadOnlySpan greenChannel, - ReadOnlySpan blueChannel, - Span source) + Span redChannel, + Span greenChannel, + Span blueChannel, + ReadOnlySpan source) { + DebugGuard.IsTrue(greenChannel.Length == redChannel.Length, nameof(greenChannel), "Channels must be of same size!"); + DebugGuard.IsTrue(blueChannel.Length == redChannel.Length, nameof(blueChannel), "Channels must be of same size!"); + DebugGuard.IsTrue(source.Length <= redChannel.Length, nameof(source), "'source' span should not be bigger than the destination channels!"); + ref float r = ref MemoryMarshal.GetReference(redChannel); ref float g = ref MemoryMarshal.GetReference(greenChannel); ref float b = ref MemoryMarshal.GetReference(blueChannel); diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.ScaledCopy.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.ScaledCopy.cs index 7edcd95da4..fe92582f47 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.ScaledCopy.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.ScaledCopy.cs @@ -1,10 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using System; using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Memory; // ReSharper disable UseObjectOrCollectionInitializer // ReSharper disable InconsistentNaming diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromCmykAvx.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykAvx.cs similarity index 97% rename from src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromCmykAvx.cs rename to src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykAvx.cs index 989d155fcd..66173b0a5a 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromCmykAvx.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykAvx.cs @@ -12,9 +12,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components { internal abstract partial class JpegColorConverterBase { - internal sealed class FromCmykAvx : JpegColorConverterAvx + internal sealed class CmykAvx : JpegColorConverterAvx { - public FromCmykAvx(int precision) + public CmykAvx(int precision) : base(JpegColorSpace.Cmyk, precision) { } diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromCmykScalar.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykScalar.cs similarity index 95% rename from src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromCmykScalar.cs rename to src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykScalar.cs index af3decaad0..44ad19687f 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromCmykScalar.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykScalar.cs @@ -7,9 +7,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components { internal abstract partial class JpegColorConverterBase { - internal sealed class FromCmykScalar : JpegColorConverterScalar + internal sealed class CmykScalar : JpegColorConverterScalar { - public FromCmykScalar(int precision) + public CmykScalar(int precision) : base(JpegColorSpace.Cmyk, precision) { } diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromCmykVector.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector.cs similarity index 93% rename from src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromCmykVector.cs rename to src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector.cs index 6a6fd4f5f5..196f64ead3 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromCmykVector.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector.cs @@ -10,9 +10,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components { internal abstract partial class JpegColorConverterBase { - internal sealed class FromCmykVector : JpegColorConverterVector + internal sealed class CmykVector : JpegColorConverterVector { - public FromCmykVector(int precision) + public CmykVector(int precision) : base(JpegColorSpace.Cmyk, precision) { } @@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components } protected override void ConvertCoreInplaceToRgb(in ComponentValues values) - => FromCmykScalar.ConvertToRgbInplace(values, this.MaximumValue); + => CmykScalar.ConvertToRgbInplace(values, this.MaximumValue); protected override void ConvertCoreVectorizedInplaceFromRgb(in ComponentValues values, Span r, Span g, Span b) { @@ -90,7 +90,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components } protected override void ConvertCoreInplaceFromRgb(in ComponentValues values, Span r, Span g, Span b) - => FromCmykScalar.ConvertFromRgbInplace(values, this.MaximumValue, r, g, b); + => CmykScalar.ConvertFromRgbInplace(values, this.MaximumValue, r, g, b); } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromGrayScaleAvx.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleAvx.cs similarity index 77% rename from src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromGrayScaleAvx.cs rename to src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleAvx.cs index faa50ba476..89d51586b8 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromGrayScaleAvx.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleAvx.cs @@ -13,15 +13,27 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components { internal abstract partial class JpegColorConverterBase { - internal sealed class FromGrayscaleAvx : JpegColorConverterAvx + internal sealed class GrayscaleAvx : JpegColorConverterAvx { - public FromGrayscaleAvx(int precision) + public GrayscaleAvx(int precision) : base(JpegColorSpace.Grayscale, precision) { } public override void ConvertToRgbInplace(in ComponentValues values) { + ref Vector256 c0Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); + + // Used for the color conversion + var scale = Vector256.Create(1 / this.MaximumValue); + + nint n = values.Component0.Length / Vector256.Count; + for (nint i = 0; i < n; i++) + { + ref Vector256 c0 = ref Unsafe.Add(ref c0Base, i); + c0 = Avx.Multiply(c0, scale); + } } public override void ConvertFromRgbInplace(in ComponentValues values, Span rLane, Span gLane, Span bLane) diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromGrayScaleScalar.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleScalar.cs similarity index 69% rename from src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromGrayScaleScalar.cs rename to src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleScalar.cs index 2430606229..4029830d5e 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromGrayScaleScalar.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleScalar.cs @@ -2,14 +2,16 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.Formats.Jpeg.Components { internal abstract partial class JpegColorConverterBase { - internal sealed class FromGrayscaleScalar : JpegColorConverterScalar + internal sealed class GrayscaleScalar : JpegColorConverterScalar { - public FromGrayscaleScalar(int precision) + public GrayscaleScalar(int precision) : base(JpegColorSpace.Grayscale, precision) { } @@ -22,6 +24,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components internal static void ConvertCoreInplaceToRgb(Span values, float maxValue) { + ref float valuesRef = ref MemoryMarshal.GetReference(values); + float scale = 1 / maxValue; + + for (nint i = 0; i < values.Length; i++) + { + Unsafe.Add(ref valuesRef, i) *= scale; + } } internal static void ConvertCoreInplaceFromRgb(in ComponentValues values, Span rLane, Span gLane, Span bLane) @@ -35,7 +44,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components float b = bLane[i]; // luminocity = (0.299 * r) + (0.587 * g) + (0.114 * b) - c0[i] = (0.299f * r) + (0.587f * g) + (0.114f * b); + float luma = (0.299f * r) + (0.587f * g) + (0.114f * b); + c0[i] = luma; } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromGrayScaleVector.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector.cs similarity index 89% rename from src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromGrayScaleVector.cs rename to src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector.cs index f92b283d42..ef51ff2b0f 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromGrayScaleVector.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector.cs @@ -10,9 +10,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components { internal abstract partial class JpegColorConverterBase { - internal sealed class FromGrayScaleVector : JpegColorConverterVector + internal sealed class GrayScaleVector : JpegColorConverterVector { - public FromGrayScaleVector(int precision) + public GrayScaleVector(int precision) : base(JpegColorSpace.Grayscale, precision) { } @@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components } protected override void ConvertCoreInplaceToRgb(in ComponentValues values) - => FromGrayscaleScalar.ConvertCoreInplaceToRgb(values.Component0, this.MaximumValue); + => GrayscaleScalar.ConvertCoreInplaceToRgb(values.Component0, this.MaximumValue); protected override void ConvertCoreVectorizedInplaceFromRgb(in ComponentValues values, Span rLane, Span gLane, Span bLane) { @@ -64,7 +64,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components } protected override void ConvertCoreInplaceFromRgb(in ComponentValues values, Span r, Span g, Span b) - => FromGrayscaleScalar.ConvertCoreInplaceFromRgb(values, r, g, b); + => GrayscaleScalar.ConvertCoreInplaceFromRgb(values, r, g, b); } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromRgbAvx.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbAvx.cs similarity index 94% rename from src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromRgbAvx.cs rename to src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbAvx.cs index 141f323ec8..6731d535ee 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromRgbAvx.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbAvx.cs @@ -12,9 +12,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components { internal abstract partial class JpegColorConverterBase { - internal sealed class FromRgbAvx : JpegColorConverterAvx + internal sealed class RgbAvx : JpegColorConverterAvx { - public FromRgbAvx(int precision) + public RgbAvx(int precision) : base(JpegColorSpace.RGB, precision) { } diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromRgbScalar.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbScalar.cs similarity index 73% rename from src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromRgbScalar.cs rename to src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbScalar.cs index 64b357d54e..42cd002427 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromRgbScalar.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbScalar.cs @@ -7,9 +7,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components { internal abstract partial class JpegColorConverterBase { - internal sealed class FromRgbScalar : JpegColorConverterScalar + internal sealed class RgbScalar : JpegColorConverterScalar { - public FromRgbScalar(int precision) + public RgbScalar(int precision) : base(JpegColorSpace.RGB, precision) { } @@ -22,9 +22,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components internal static void ConvertCoreInplaceToRgb(ComponentValues values, float maxValue) { - FromGrayscaleScalar.ConvertCoreInplaceToRgb(values.Component0, maxValue); - FromGrayscaleScalar.ConvertCoreInplaceToRgb(values.Component1, maxValue); - FromGrayscaleScalar.ConvertCoreInplaceToRgb(values.Component2, maxValue); + GrayscaleScalar.ConvertCoreInplaceToRgb(values.Component0, maxValue); + GrayscaleScalar.ConvertCoreInplaceToRgb(values.Component1, maxValue); + GrayscaleScalar.ConvertCoreInplaceToRgb(values.Component2, maxValue); } internal static void ConvertCoreInplaceFromRgb(ComponentValues values, Span r, Span g, Span b) diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromRgbVector.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbVector.cs similarity index 88% rename from src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromRgbVector.cs rename to src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbVector.cs index 72a8ef1298..f4c4fa379c 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromRgbVector.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbVector.cs @@ -10,9 +10,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components { internal abstract partial class JpegColorConverterBase { - internal sealed class FromRgbVector : JpegColorConverterVector + internal sealed class RgbVector : JpegColorConverterVector { - public FromRgbVector(int precision) + public RgbVector(int precision) : base(JpegColorSpace.RGB, precision) { } @@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components } protected override void ConvertCoreInplaceToRgb(in ComponentValues values) - => FromRgbScalar.ConvertCoreInplaceToRgb(values, this.MaximumValue); + => RgbScalar.ConvertCoreInplaceToRgb(values, this.MaximumValue); protected override void ConvertCoreVectorizedInplaceFromRgb(in ComponentValues values, Span r, Span g, Span b) { @@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components } protected override void ConvertCoreInplaceFromRgb(in ComponentValues values, Span r, Span g, Span b) - => FromRgbScalar.ConvertCoreInplaceFromRgb(values, r, g, b); + => RgbScalar.ConvertCoreInplaceFromRgb(values, r, g, b); } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYCbCrAvx.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrAvx.cs similarity index 93% rename from src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYCbCrAvx.cs rename to src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrAvx.cs index 678df453c8..46e9fd033e 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYCbCrAvx.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrAvx.cs @@ -14,9 +14,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components { internal abstract partial class JpegColorConverterBase { - internal sealed class FromYCbCrAvx : JpegColorConverterAvx + internal sealed class YCbCrAvx : JpegColorConverterAvx { - public FromYCbCrAvx(int precision) + public YCbCrAvx(int precision) : base(JpegColorSpace.YCbCr, precision) { } @@ -33,10 +33,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components // Used for the color conversion var chromaOffset = Vector256.Create(-this.HalfValue); var scale = Vector256.Create(1 / this.MaximumValue); - var rCrMult = Vector256.Create(FromYCbCrScalar.RCrMult); - var gCbMult = Vector256.Create(-FromYCbCrScalar.GCbMult); - var gCrMult = Vector256.Create(-FromYCbCrScalar.GCrMult); - var bCbMult = Vector256.Create(FromYCbCrScalar.BCbMult); + var rCrMult = Vector256.Create(YCbCrScalar.RCrMult); + var gCbMult = Vector256.Create(-YCbCrScalar.GCbMult); + var gCrMult = Vector256.Create(-YCbCrScalar.GCrMult); + var bCbMult = Vector256.Create(YCbCrScalar.BCbMult); // Walking 8 elements at one step: nint n = values.Component0.Length / Vector256.Count; diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYCbCrScalar.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrScalar.cs similarity index 96% rename from src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYCbCrScalar.cs rename to src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrScalar.cs index 424ab83eb3..e5f76dadfd 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYCbCrScalar.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrScalar.cs @@ -7,7 +7,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components { internal abstract partial class JpegColorConverterBase { - internal sealed class FromYCbCrScalar : JpegColorConverterScalar + internal sealed class YCbCrScalar : JpegColorConverterScalar { // TODO: comments, derived from ITU-T Rec. T.871 internal const float RCrMult = 1.402f; @@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components internal const float GCrMult = (float)(0.299 * 1.402 / 0.587); internal const float BCbMult = 1.772f; - public FromYCbCrScalar(int precision) + public YCbCrScalar(int precision) : base(JpegColorSpace.YCbCr, precision) { } diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYCbCrVector.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector.cs similarity index 89% rename from src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYCbCrVector.cs rename to src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector.cs index 022c09b497..bf0aa4a341 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYCbCrVector.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector.cs @@ -11,9 +11,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components { internal abstract partial class JpegColorConverterBase { - internal sealed class FromYCbCrVector : JpegColorConverterVector + internal sealed class YCbCrVector : JpegColorConverterVector { - public FromYCbCrVector(int precision) + public YCbCrVector(int precision) : base(JpegColorSpace.YCbCr, precision) { } @@ -30,10 +30,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components var chromaOffset = new Vector(-this.HalfValue); var scale = new Vector(1 / this.MaximumValue); - var rCrMult = new Vector(FromYCbCrScalar.RCrMult); - var gCbMult = new Vector(-FromYCbCrScalar.GCbMult); - var gCrMult = new Vector(-FromYCbCrScalar.GCrMult); - var bCbMult = new Vector(FromYCbCrScalar.BCbMult); + var rCrMult = new Vector(YCbCrScalar.RCrMult); + var gCbMult = new Vector(-YCbCrScalar.GCbMult); + var gCrMult = new Vector(-YCbCrScalar.GCrMult); + var bCbMult = new Vector(YCbCrScalar.BCbMult); nint n = values.Component0.Length / Vector.Count; for (nint i = 0; i < n; i++) @@ -69,7 +69,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components } protected override void ConvertCoreInplaceToRgb(in ComponentValues values) - => FromYCbCrScalar.ConvertCoreInplaceToRgb(values, this.MaximumValue, this.HalfValue); + => YCbCrScalar.ConvertCoreInplaceToRgb(values, this.MaximumValue, this.HalfValue); protected override void ConvertCoreVectorizedInplaceFromRgb(in ComponentValues values, Span rLane, Span gLane, Span bLane) { @@ -118,7 +118,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components } protected override void ConvertCoreInplaceFromRgb(in ComponentValues values, Span r, Span g, Span b) - => FromYCbCrScalar.ConvertCoreInplaceFromRgb(values, this.HalfValue, r, g, b); + => YCbCrScalar.ConvertCoreInplaceFromRgb(values, this.HalfValue, r, g, b); } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYccKAvx.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKAvx.cs similarity index 89% rename from src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYccKAvx.cs rename to src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKAvx.cs index 6166f342b3..58d024e376 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYccKAvx.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKAvx.cs @@ -13,9 +13,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components { internal abstract partial class JpegColorConverterBase { - internal sealed class FromYccKAvx : JpegColorConverterAvx + internal sealed class YccKAvx : JpegColorConverterAvx { - public FromYccKAvx(int precision) + public YccKAvx(int precision) : base(JpegColorSpace.Ycck, precision) { } @@ -35,10 +35,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components var chromaOffset = Vector256.Create(-this.HalfValue); var scale = Vector256.Create(1 / (this.MaximumValue * this.MaximumValue)); var max = Vector256.Create(this.MaximumValue); - var rCrMult = Vector256.Create(FromYCbCrScalar.RCrMult); - var gCbMult = Vector256.Create(-FromYCbCrScalar.GCbMult); - var gCrMult = Vector256.Create(-FromYCbCrScalar.GCrMult); - var bCbMult = Vector256.Create(FromYCbCrScalar.BCbMult); + var rCrMult = Vector256.Create(YCbCrScalar.RCrMult); + var gCbMult = Vector256.Create(-YCbCrScalar.GCbMult); + var gCrMult = Vector256.Create(-YCbCrScalar.GCrMult); + var bCbMult = Vector256.Create(YCbCrScalar.BCbMult); // Walking 8 elements at one step: nint n = values.Component0.Length / Vector256.Count; diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYccKScalar.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKScalar.cs similarity index 93% rename from src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYccKScalar.cs rename to src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKScalar.cs index bf5d1c4140..f49e819b9e 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYccKScalar.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKScalar.cs @@ -7,9 +7,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components { internal abstract partial class JpegColorConverterBase { - internal sealed class FromYccKScalar : JpegColorConverterScalar + internal sealed class YccKScalar : JpegColorConverterScalar { - public FromYccKScalar(int precision) + public YccKScalar(int precision) : base(JpegColorSpace.Ycck, precision) { } diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYccKVector.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKVector.cs similarity index 86% rename from src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYccKVector.cs rename to src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKVector.cs index cc4212d473..7a5597c463 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYccKVector.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKVector.cs @@ -10,9 +10,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components { internal abstract partial class JpegColorConverterBase { - internal sealed class FromYccKVector : JpegColorConverterVector + internal sealed class YccKVector : JpegColorConverterVector { - public FromYccKVector(int precision) + public YccKVector(int precision) : base(JpegColorSpace.Ycck, precision) { } @@ -31,10 +31,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components var chromaOffset = new Vector(-this.HalfValue); var scale = new Vector(1 / (this.MaximumValue * this.MaximumValue)); var max = new Vector(this.MaximumValue); - var rCrMult = new Vector(FromYCbCrScalar.RCrMult); - var gCbMult = new Vector(-FromYCbCrScalar.GCbMult); - var gCrMult = new Vector(-FromYCbCrScalar.GCrMult); - var bCbMult = new Vector(FromYCbCrScalar.BCbMult); + var rCrMult = new Vector(YCbCrScalar.RCrMult); + var gCbMult = new Vector(-YCbCrScalar.GCbMult); + var gCrMult = new Vector(-YCbCrScalar.GCrMult); + var bCbMult = new Vector(YCbCrScalar.BCbMult); nint n = values.Component0.Length / Vector.Count; for (nint i = 0; i < n; i++) @@ -70,7 +70,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components } protected override void ConvertCoreInplaceToRgb(in ComponentValues values) => - FromYccKScalar.ConvertToRgpInplace(values, this.MaximumValue, this.HalfValue); + YccKScalar.ConvertToRgpInplace(values, this.MaximumValue, this.HalfValue); protected override void ConvertCoreVectorizedInplaceFromRgb(in ComponentValues values, Span r, Span g, Span b) => throw new System.NotImplementedException(); diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs index 500c25a951..744c178e89 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs @@ -110,9 +110,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components /// private static IEnumerable GetYCbCrConverters(int precision) { - yield return new FromYCbCrAvx(precision); - yield return new FromYCbCrVector(precision); - yield return new FromYCbCrScalar(precision); + yield return new YCbCrAvx(precision); + yield return new YCbCrVector(precision); + yield return new YCbCrScalar(precision); } /// @@ -120,9 +120,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components /// private static IEnumerable GetYccKConverters(int precision) { - yield return new FromYccKAvx(precision); - yield return new FromYccKVector(precision); - yield return new FromYccKScalar(precision); + yield return new YccKAvx(precision); + yield return new YccKVector(precision); + yield return new YccKScalar(precision); } /// @@ -130,9 +130,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components /// private static IEnumerable GetCmykConverters(int precision) { - yield return new FromCmykAvx(precision); - yield return new FromCmykVector(precision); - yield return new FromCmykScalar(precision); + yield return new CmykAvx(precision); + yield return new CmykVector(precision); + yield return new CmykScalar(precision); } /// @@ -140,9 +140,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components /// private static IEnumerable GetGrayScaleConverters(int precision) { - yield return new FromGrayscaleAvx(precision); - yield return new FromGrayScaleVector(precision); - yield return new FromGrayscaleScalar(precision); + yield return new GrayscaleAvx(precision); + yield return new GrayScaleVector(precision); + yield return new GrayscaleScalar(precision); } /// @@ -150,9 +150,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components /// private static IEnumerable GetRgbConverters(int precision) { - yield return new FromRgbAvx(precision); - yield return new FromRgbVector(precision); - yield return new FromRgbScalar(precision); + yield return new RgbAvx(precision); + yield return new RgbVector(precision); + yield return new RgbScalar(precision); } /// @@ -230,7 +230,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components /// /// List of component color processors. /// Row to convert - public ComponentValues(IReadOnlyList processors, int row) + public ComponentValues(IReadOnlyList processors, int row) { DebugGuard.MustBeGreaterThan(processors.Count, 0, nameof(processors)); diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/JpegComponent.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/Component.cs similarity index 95% rename from src/ImageSharp/Formats/Jpeg/Components/Encoder/JpegComponent.cs rename to src/ImageSharp/Formats/Jpeg/Components/Encoder/Component.cs index 49c7d4257e..ccb4413abd 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/JpegComponent.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/Component.cs @@ -9,11 +9,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder /// /// Represents a single frame component. /// - internal class JpegComponent : IDisposable + internal class Component : IDisposable { private readonly MemoryAllocator memoryAllocator; - public JpegComponent(MemoryAllocator memoryAllocator, int horizontalFactor, int verticalFactor, int quantizationTableIndex) + public Component(MemoryAllocator memoryAllocator, int horizontalFactor, int verticalFactor, int quantizationTableIndex) { this.memoryAllocator = memoryAllocator; diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/JpegComponentPostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/ComponentProcessor.cs similarity index 90% rename from src/ImageSharp/Formats/Jpeg/Components/Encoder/JpegComponentPostProcessor.cs rename to src/ImageSharp/Formats/Jpeg/Components/Encoder/ComponentProcessor.cs index fb9f72a1a1..400ce41e24 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/JpegComponentPostProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/ComponentProcessor.cs @@ -11,26 +11,27 @@ using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder { - internal class JpegComponentPostProcessor : IDisposable + internal class ComponentProcessor : IDisposable { private readonly Size blockAreaSize; - private readonly JpegComponent component; + private readonly Component component; private Block8x8F quantTable; - public JpegComponentPostProcessor(MemoryAllocator memoryAllocator, JpegComponent component, Size postProcessorBufferSize, Block8x8F quantTable) + public ComponentProcessor(MemoryAllocator memoryAllocator, Component component, Size postProcessorBufferSize, Block8x8F quantTable) { this.component = component; this.quantTable = quantTable; - FastFloatingPointDCT.AdjustToFDCT(ref this.quantTable); this.component = component; this.blockAreaSize = component.SubSamplingDivisors * 8; + + // alignment of 8 so each block stride can be sampled from a single 'ref pointer' this.ColorBuffer = memoryAllocator.Allocate2DOveraligned( postProcessorBufferSize.Width, postProcessorBufferSize.Height, - 8 * component.SubSamplingDivisors.Height, + 8, AllocationOptions.Clean); } @@ -47,7 +48,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder // but 12-bit jpegs are not supported currently float normalizationValue = -128f; - int destAreaStride = this.ColorBuffer.Width * this.component.SubSamplingDivisors.Height; + int destAreaStride = this.ColorBuffer.Width; int yBlockStart = spectralStep * this.component.SamplingFactors.Height; @@ -97,22 +98,27 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder { Size factors = this.component.SubSamplingDivisors; + int packedWidth = this.ColorBuffer.Width / factors.Width; + float averageMultiplier = 1f / (factors.Width * factors.Height); for (int i = 0; i < this.ColorBuffer.Height; i += factors.Height) { - Span targetBufferRow = this.ColorBuffer.DangerousGetRowSpan(i); + Span sourceRow = this.ColorBuffer.DangerousGetRowSpan(i); // vertical sum for (int j = 1; j < factors.Height; j++) { - SumVertical(targetBufferRow, this.ColorBuffer.DangerousGetRowSpan(i + j)); + SumVertical(sourceRow, this.ColorBuffer.DangerousGetRowSpan(i + j)); } // horizontal sum - SumHorizontal(targetBufferRow, factors.Width); + SumHorizontal(sourceRow, factors.Width); // calculate average - MultiplyToAverage(targetBufferRow, averageMultiplier); + MultiplyToAverage(sourceRow, averageMultiplier); + + // copy to the first 8 slots + sourceRow.Slice(0, packedWidth).CopyTo(this.ColorBuffer.DangerousGetRowSpan(i / factors.Height)); } static void SumVertical(Span target, Span source) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/EncodingConfigs/JpegComponentConfig.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/EncodingConfigs/JpegComponentConfig.cs new file mode 100644 index 0000000000..b9a589e21f --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/EncodingConfigs/JpegComponentConfig.cs @@ -0,0 +1,30 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder +{ + internal class JpegComponentConfig + { + public JpegComponentConfig(byte id, int hsf, int vsf, int quantIndex, int dcIndex, int acIndex) + { + this.Id = id; + this.HorizontalSampleFactor = hsf; + this.VerticalSampleFactor = vsf; + this.QuantizatioTableIndex = quantIndex; + this.DcTableSelector = dcIndex; + this.AcTableSelector = acIndex; + } + + public byte Id { get; } + + public int HorizontalSampleFactor { get; } + + public int VerticalSampleFactor { get; } + + public int QuantizatioTableIndex { get; } + + public int DcTableSelector { get; } + + public int AcTableSelector { get; } + } +} diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/EncodingConfigs/JpegFrameConfig.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/EncodingConfigs/JpegFrameConfig.cs new file mode 100644 index 0000000000..0bb0f17d13 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/EncodingConfigs/JpegFrameConfig.cs @@ -0,0 +1,42 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder +{ + internal class JpegFrameConfig + { + public JpegFrameConfig(JpegColorSpace colorType, JpegEncodingColor encodingColor, JpegComponentConfig[] components, JpegHuffmanTableConfig[] huffmanTables, JpegQuantizationTableConfig[] quantTables) + { + this.ColorType = colorType; + this.EncodingColor = encodingColor; + this.Components = components; + this.HuffmanTables = huffmanTables; + this.QuantizationTables = quantTables; + + this.MaxHorizontalSamplingFactor = components[0].HorizontalSampleFactor; + this.MaxVerticalSamplingFactor = components[0].VerticalSampleFactor; + for (int i = 1; i < components.Length; i++) + { + JpegComponentConfig component = components[i]; + this.MaxHorizontalSamplingFactor = Math.Max(this.MaxHorizontalSamplingFactor, component.HorizontalSampleFactor); + this.MaxVerticalSamplingFactor = Math.Max(this.MaxVerticalSamplingFactor, component.VerticalSampleFactor); + } + } + + public JpegColorSpace ColorType { get; } + + public JpegEncodingColor EncodingColor { get; } + + public JpegComponentConfig[] Components { get; } + + public JpegHuffmanTableConfig[] HuffmanTables { get; } + + public JpegQuantizationTableConfig[] QuantizationTables { get; } + + public int MaxHorizontalSamplingFactor { get; } + + public int MaxVerticalSamplingFactor { get; } + } +} diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/EncodingConfigs/JpegHuffmanTableConfig.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/EncodingConfigs/JpegHuffmanTableConfig.cs new file mode 100644 index 0000000000..d0c3038db7 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/EncodingConfigs/JpegHuffmanTableConfig.cs @@ -0,0 +1,21 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder +{ + internal class JpegHuffmanTableConfig + { + public JpegHuffmanTableConfig(int @class, int destIndex, HuffmanSpec table) + { + this.Class = @class; + this.DestinationIndex = destIndex; + this.Table = table; + } + + public int Class { get; } + + public int DestinationIndex { get; } + + public HuffmanSpec Table { get; } + } +} diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/EncodingConfigs/JpegQuantizationTableConfig.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/EncodingConfigs/JpegQuantizationTableConfig.cs new file mode 100644 index 0000000000..29f0c05db8 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/EncodingConfigs/JpegQuantizationTableConfig.cs @@ -0,0 +1,20 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder +{ + internal class JpegQuantizationTableConfig + { + public JpegQuantizationTableConfig(int destIndex, ReadOnlySpan quantizationTable) + { + this.DestinationIndex = destIndex; + this.Table = Block8x8.Load(quantizationTable); + } + + public int DestinationIndex { get; } + + public Block8x8 Table { get; } + } +} diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs index c989346fcb..08beb46331 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs @@ -135,6 +135,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder tables[tableConfig.DestinationIndex] = new HuffmanLut(tableConfig.Table); } + /// + /// Encodes scan in baseline interleaved mode. + /// + /// Output color space. + /// Frame to encode. + /// Converter from color to spectral. + /// The token to request cancellation. public void EncodeScanBaselineInterleaved(JpegEncodingColor color, JpegFrame frame, SpectralConverter converter, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { @@ -142,20 +149,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder { case JpegEncodingColor.YCbCrRatio444: case JpegEncodingColor.Rgb: - this.EncodeScanBaselineInterleaved444(frame, converter, cancellationToken); - break; - case JpegEncodingColor.YCbCrRatio420: - this.EncodeScanBaselineInterleaved420(frame, converter, cancellationToken); + this.EncodeThreeComponentScanBaselineInterleaved444(frame, converter, cancellationToken); break; default: - this.EncodeScanBaselineInterleavedArbitrarySampling(frame, converter, cancellationToken); + this.EncodeScanBaselineInterleaved(frame, converter, cancellationToken); break; } - - this.FlushRemainingBytes(); } - public void EncodeScanBaselineSingleComponent(JpegComponent component, SpectralConverter converter, CancellationToken cancellationToken) + /// + /// Encodes grayscale scan in baseline interleaved mode. + /// + /// Component with grayscale data. + /// Converter from color to spectral. + /// The token to request cancellation. + public void EncodeScanBaselineSingleComponent(Component component, SpectralConverter converter, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { int h = component.HeightInBlocks; @@ -189,9 +197,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder } } } + + this.FlushRemainingBytes(); } - public void EncodeScanBaseline(JpegComponent component, CancellationToken cancellationToken) + /// + /// Encodes scan with a single component in baseline non-interleaved mode. + /// + /// Component with grayscale data. + /// The token to request cancellation. + public void EncodeScanBaseline(Component component, CancellationToken cancellationToken) { int h = component.HeightInBlocks; int w = component.WidthInBlocks; @@ -225,7 +240,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder this.FlushRemainingBytes(); } - private void EncodeScanBaselineInterleavedArbitrarySampling(JpegFrame frame, SpectralConverter converter, CancellationToken cancellationToken) + /// + /// Encodes scan in baseline interleaved mode for any amount of component with arbitrary sampling factors. + /// + /// Frame to encode. + /// Converter from color to spectral. + /// The token to request cancellation. + private void EncodeScanBaselineInterleaved(JpegFrame frame, SpectralConverter converter, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { int mcu = 0; @@ -246,7 +267,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder int mcuCol = mcu % mcusPerLine; for (int k = 0; k < frame.Components.Length; k++) { - JpegComponent component = frame.Components[k]; + Component component = frame.Components[k]; ref HuffmanLut dcHuffmanTable = ref this.dcHuffmanTables[component.DcTableId]; ref HuffmanLut acHuffmanTable = ref this.acHuffmanTables[component.AcTableId]; @@ -282,17 +303,25 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder } } } + + this.FlushRemainingBytes(); } - private void EncodeScanBaselineInterleaved444(JpegFrame frame, SpectralConverter converter, CancellationToken cancellationToken) + /// + /// Encodes scan in baseline interleaved mode with exactly 3 components with 4:4:4 sampling. + /// + /// Frame to encode. + /// Converter from color to spectral. + /// The token to request cancellation. + private void EncodeThreeComponentScanBaselineInterleaved444(JpegFrame frame, SpectralConverter converter, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { int mcusPerColumn = frame.McusPerColumn; int mcusPerLine = frame.McusPerLine; - JpegComponent c2 = frame.Components[2]; - JpegComponent c1 = frame.Components[1]; - JpegComponent c0 = frame.Components[0]; + Component c2 = frame.Components[2]; + Component c1 = frame.Components[1]; + Component c0 = frame.Components[0]; ref HuffmanLut c0dcHuffmanTable = ref this.dcHuffmanTables[c0.DcTableId]; ref HuffmanLut c0acHuffmanTable = ref this.acHuffmanTables[c0.AcTableId]; @@ -339,16 +368,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder } } } - } - private void EncodeScanBaselineInterleaved420(JpegFrame frame, SpectralConverter converter, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel - { - throw new NotImplementedException(); + this.FlushRemainingBytes(); } private void WriteBlock( - JpegComponent component, + Component component, ref Block8x8 block, ref HuffmanLut dcTable, ref HuffmanLut acTable) @@ -611,7 +636,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder /// /// Flushes spectral data bytes after encoding all channel blocks - /// in a single jpeg macroblock using . + /// in a single jpeg macroblock using . /// /// /// This must be called only if is true @@ -641,7 +666,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder int lastByteIndex = (this.emitWriteIndex * 4) - valuableBytesCount; this.FlushToStream(lastByteIndex); - // Clean huffman register + // Clear huffman register // This is needed for for images with multiples scans this.bitCount = 0; this.accumulatedBits = 0; diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/JpegFrame.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/JpegFrame.cs index a6a9bcb0a9..d45de2c16a 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/JpegFrame.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/JpegFrame.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder @@ -11,19 +12,23 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder /// internal sealed class JpegFrame : IDisposable { - public JpegFrame(JpegFrameConfig frameConfig, MemoryAllocator allocator, Image image, JpegColorSpace colorSpace) + public JpegFrame(Image image, JpegFrameConfig frameConfig, bool interleaved) { - this.ColorSpace = colorSpace; + this.ColorSpace = frameConfig.ColorType; + + this.Interleaved = interleaved; this.PixelWidth = image.Width; this.PixelHeight = image.Height; + MemoryAllocator allocator = image.GetConfiguration().MemoryAllocator; + JpegComponentConfig[] componentConfigs = frameConfig.Components; - this.Components = new JpegComponent[componentConfigs.Length]; + this.Components = new Component[componentConfigs.Length]; for (int i = 0; i < this.Components.Length; i++) { JpegComponentConfig componentConfig = componentConfigs[i]; - this.Components[i] = new JpegComponent(allocator, componentConfig.HorizontalSampleFactor, componentConfig.VerticalSampleFactor, componentConfig.QuantizatioTableIndex) + this.Components[i] = new Component(allocator, componentConfig.HorizontalSampleFactor, componentConfig.VerticalSampleFactor, componentConfig.QuantizatioTableIndex) { DcTableId = componentConfig.DcTableSelector, AcTableId = componentConfig.AcTableSelector, @@ -39,18 +44,20 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder for (int i = 0; i < this.Components.Length; i++) { - JpegComponent component = this.Components[i]; + Component component = this.Components[i]; component.Init(this, maxSubFactorH, maxSubFactorV); } } public JpegColorSpace ColorSpace { get; } - public int PixelHeight { get; private set; } + public bool Interleaved { get; } + + public int PixelHeight { get; } - public int PixelWidth { get; private set; } + public int PixelWidth { get; } - public JpegComponent[] Components { get; } + public Component[] Components { get; } public int McusPerLine { get; } @@ -62,7 +69,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder { for (int i = 0; i < this.Components.Length; i++) { - this.Components[i]?.Dispose(); + this.Components[i].Dispose(); } } @@ -70,7 +77,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder { for (int i = 0; i < this.Components.Length; i++) { - JpegComponent component = this.Components[i]; + Component component = this.Components[i]; component.AllocateSpectral(fullScan); } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/SpectralConverter{TPixel}.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/SpectralConverter{TPixel}.cs index 3445072858..18c0e63cfc 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/SpectralConverter{TPixel}.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/SpectralConverter{TPixel}.cs @@ -4,40 +4,37 @@ using System; using System.Buffers; using System.Linq; +using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder { /// - internal class SpectralConverter : SpectralConverter + internal class SpectralConverter : SpectralConverter, IDisposable where TPixel : unmanaged, IPixel { - private readonly Configuration configuration; + private readonly ComponentProcessor[] componentProcessors; - private JpegComponentPostProcessor[] componentProcessors; - - private int pixelRowsPerStep; + private readonly int pixelRowsPerStep; private int pixelRowCounter; - private Buffer2D pixelBuffer; + private readonly Buffer2D pixelBuffer; - private IMemoryOwner redLane; + private readonly IMemoryOwner redLane; - private IMemoryOwner greenLane; + private readonly IMemoryOwner greenLane; - private IMemoryOwner blueLane; + private readonly IMemoryOwner blueLane; - private int alignedPixelWidth; + private readonly int alignedPixelWidth; - private JpegColorConverterBase colorConverter; + private readonly JpegColorConverterBase colorConverter; - public SpectralConverter(JpegFrame frame, Image image, Block8x8F[] dequantTables, Configuration configuration) + public SpectralConverter(JpegFrame frame, Image image, Block8x8F[] dequantTables) { - this.configuration = configuration; - - MemoryAllocator allocator = this.configuration.MemoryAllocator; + MemoryAllocator allocator = image.GetConfiguration().MemoryAllocator; // iteration data int majorBlockWidth = frame.Components.Max((component) => component.SizeInBlocks.Width); @@ -47,23 +44,26 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder this.pixelRowsPerStep = majorVerticalSamplingFactor * blockPixelHeight; // pixel buffer of the image - // currently codec only supports encoding single frame jpegs this.pixelBuffer = image.GetRootFramePixelBuffer(); - // component processors from spectral to Rgba32 + // component processors from spectral to Rgb24 const int blockPixelWidth = 8; this.alignedPixelWidth = majorBlockWidth * blockPixelWidth; var postProcessorBufferSize = new Size(this.alignedPixelWidth, this.pixelRowsPerStep); - this.componentProcessors = new JpegComponentPostProcessor[frame.Components.Length]; + this.componentProcessors = new ComponentProcessor[frame.Components.Length]; for (int i = 0; i < this.componentProcessors.Length; i++) { - JpegComponent component = frame.Components[i]; - this.componentProcessors[i] = new JpegComponentPostProcessor(allocator, component, postProcessorBufferSize, dequantTables[component.QuantizationTableIndex]); + Component component = frame.Components[i]; + this.componentProcessors[i] = new ComponentProcessor( + allocator, + component, + postProcessorBufferSize, + dequantTables[component.QuantizationTableIndex]); } - this.redLane = allocator.Allocate(this.alignedPixelWidth); - this.greenLane = allocator.Allocate(this.alignedPixelWidth); - this.blueLane = allocator.Allocate(this.alignedPixelWidth); + this.redLane = allocator.Allocate(this.alignedPixelWidth, AllocationOptions.Clean); + this.greenLane = allocator.Allocate(this.alignedPixelWidth, AllocationOptions.Clean); + this.blueLane = allocator.Allocate(this.alignedPixelWidth, AllocationOptions.Clean); // color converter from Rgb24 to YCbCr this.colorConverter = JpegColorConverterBase.GetConverter(colorSpace: frame.ColorSpace, precision: 8); @@ -71,10 +71,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder public void ConvertStrideBaseline() { + // Codestyle suggests expression body but it + // also requires empty line before comments + // which looks ugly with expression bodies thus this warning disable +#pragma warning disable IDE0022 // Convert next pixel stride using single spectral `stride' // Note that zero passing eliminates the need of virtual call // from JpegComponentPostProcessor this.ConvertStride(spectralStep: 0); +#pragma warning restore IDE0022 } public void ConvertFull() @@ -88,34 +93,48 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder private void ConvertStride(int spectralStep) { - // 1. Unpack from TPixel to r/g/b planes - // 2. Byte r/g/b planes to normalized float r/g/b planes - // 3. Convert from r/g/b planes to target pixel type with JpegColorConverter - // 4. Convert color buffer to spectral blocks with component post processors - int maxY = Math.Min(this.pixelBuffer.Height, this.pixelRowCounter + this.pixelRowsPerStep); + int start = this.pixelRowCounter; + int end = start + this.pixelRowsPerStep; + + int pixelBufferLastVerticalIndex = this.pixelBuffer.Height - 1; Span rLane = this.redLane.GetSpan(); Span gLane = this.greenLane.GetSpan(); Span bLane = this.blueLane.GetSpan(); - for (int yy = this.pixelRowCounter; yy < maxY; yy++) + for (int yy = start; yy < end; yy++) { int y = yy - this.pixelRowCounter; - // unpack TPixel to r/g/b planes - Span sourceRow = this.pixelBuffer.DangerousGetRowSpan(yy); - - PixelOperations.Instance.UnpackIntoRgbPlanes(this.configuration, rLane, gLane, bLane, sourceRow); + // Unpack TPixel to r/g/b planes + int srcIndex = Math.Min(yy, pixelBufferLastVerticalIndex); + Span sourceRow = this.pixelBuffer.DangerousGetRowSpan(srcIndex); + PixelOperations.Instance.UnpackIntoRgbPlanes(rLane, gLane, bLane, sourceRow); + // Convert from rgb24 to target pixel type var values = new JpegColorConverterBase.ComponentValues(this.componentProcessors, y); this.colorConverter.ConvertFromRgbInplace(values, rLane, gLane, bLane); } + // Convert pixels to spectral for (int i = 0; i < this.componentProcessors.Length; i++) { this.componentProcessors[i].CopyColorBufferToBlocks(spectralStep); } - this.pixelRowCounter += this.pixelRowsPerStep; + this.pixelRowCounter = end; + } + + /// + public void Dispose() + { + foreach (ComponentProcessor cpp in this.componentProcessors) + { + cpp.Dispose(); + } + + this.redLane.Dispose(); + this.greenLane.Dispose(); + this.blueLane.Dispose(); } } } diff --git a/src/ImageSharp/Formats/Jpeg/IJpegEncoderOptions.cs b/src/ImageSharp/Formats/Jpeg/IJpegEncoderOptions.cs index de55904212..449db9d81b 100644 --- a/src/ImageSharp/Formats/Jpeg/IJpegEncoderOptions.cs +++ b/src/ImageSharp/Formats/Jpeg/IJpegEncoderOptions.cs @@ -23,5 +23,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// Non-interleaved encoding mode encodes each color component in a separate scan. /// public bool? Interleaved { get; set; } + + /// + /// Gets or sets jpeg color for encoding. + /// + public JpegEncodingColor? ColorType { get; set; } } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 25e31a2362..0efbedff1d 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -564,17 +564,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg { return JpegEncodingColor.YCbCrRatio444; } - else if (this.Frame.Components[0].HorizontalSamplingFactor == 2 && this.Frame.Components[0].VerticalSamplingFactor == 2 && + else if (this.Frame.Components[0].HorizontalSamplingFactor == 2 && this.Frame.Components[0].VerticalSamplingFactor == 1 && this.Frame.Components[1].HorizontalSamplingFactor == 1 && this.Frame.Components[1].VerticalSamplingFactor == 1 && this.Frame.Components[2].HorizontalSamplingFactor == 1 && this.Frame.Components[2].VerticalSamplingFactor == 1) { - return JpegEncodingColor.YCbCrRatio420; + return JpegEncodingColor.YCbCrRatio422; } - else if (this.Frame.Components[0].HorizontalSamplingFactor == 1 && this.Frame.Components[0].VerticalSamplingFactor == 1 && - this.Frame.Components[1].HorizontalSamplingFactor == 1 && this.Frame.Components[1].VerticalSamplingFactor == 2 && - this.Frame.Components[2].HorizontalSamplingFactor == 1 && this.Frame.Components[2].VerticalSamplingFactor == 2) + else if (this.Frame.Components[0].HorizontalSamplingFactor == 2 && this.Frame.Components[0].VerticalSamplingFactor == 2 && + this.Frame.Components[1].HorizontalSamplingFactor == 1 && this.Frame.Components[1].VerticalSamplingFactor == 1 && + this.Frame.Components[2].HorizontalSamplingFactor == 1 && this.Frame.Components[2].VerticalSamplingFactor == 1) { - return JpegEncodingColor.YCbCrRatio422; + return JpegEncodingColor.YCbCrRatio420; } else if (this.Frame.Components[0].HorizontalSamplingFactor == 4 && this.Frame.Components[0].VerticalSamplingFactor == 1 && this.Frame.Components[1].HorizontalSamplingFactor == 1 && this.Frame.Components[1].VerticalSamplingFactor == 1 && @@ -595,7 +595,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg case JpegColorSpace.Cmyk: return JpegEncodingColor.Cmyk; + case JpegColorSpace.Ycck: + // TODO: change this after YccK encoding is implemented + // We are deliberately mapping YccK color space to Cmyk color space at metadata + // level so encoder can fallback to cmyk color space from it. + // YccK -> Cmyk is the closest conversion logically wise + return JpegEncodingColor.Cmyk; default: return JpegEncodingColor.YCbCrRatio420; } diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs index c2a50b37e2..5817eaa335 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs @@ -5,7 +5,6 @@ using System; using System.IO; using System.Threading; using System.Threading.Tasks; -using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder; using SixLabors.ImageSharp.PixelFormats; @@ -17,36 +16,31 @@ namespace SixLabors.ImageSharp.Formats.Jpeg public sealed class JpegEncoder : IImageEncoder, IJpegEncoderOptions { /// - /// The available encodable frame configs. + /// Backing field for . /// - private static readonly JpegFrameConfig[] FrameConfigs = CreateFrameConfigs(); + private int? quality; /// - public int? Quality { get; set; } - - /// - public bool? Interleaved { get; set; } - - /// - /// Sets jpeg color for encoding. - /// - public JpegEncodingColor ColorType + public int? Quality { + get => this.quality; set { - JpegFrameConfig frameConfig = Array.Find( - FrameConfigs, - cfg => cfg.EncodingColor == value); - - if (frameConfig is null) + if (value is < 1 or > 100) { - throw new ArgumentException(nameof(value)); + throw new ArgumentException("Quality factor must be in [1..100] range."); } - this.FrameConfig = frameConfig; + this.quality = value; } } + /// + public bool? Interleaved { get; set; } + + /// + public JpegEncodingColor? ColorType { get; set; } + internal JpegFrameConfig FrameConfig { get; set; } /// @@ -58,7 +52,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg public void Encode(Image image, Stream stream) where TPixel : unmanaged, IPixel { - var encoder = new JpegEncoderCore(this, this.FrameConfig); + var encoder = new JpegEncoderCore(this); encoder.Encode(image, stream); } @@ -73,249 +67,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg public Task EncodeAsync(Image image, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { - var encoder = new JpegEncoderCore(this, this.FrameConfig); + var encoder = new JpegEncoderCore(this); return encoder.EncodeAsync(image, stream, cancellationToken); } - - private static JpegFrameConfig[] CreateFrameConfigs() - { - var defaultLuminanceHuffmanDC = new JpegHuffmanTableConfig(@class: 0, destIndex: 0, HuffmanSpec.TheHuffmanSpecs[0]); - var defaultLuminanceHuffmanAC = new JpegHuffmanTableConfig(@class: 1, destIndex: 0, HuffmanSpec.TheHuffmanSpecs[1]); - var defaultChrominanceHuffmanDC = new JpegHuffmanTableConfig(@class: 0, destIndex: 1, HuffmanSpec.TheHuffmanSpecs[2]); - var defaultChrominanceHuffmanAC = new JpegHuffmanTableConfig(@class: 1, destIndex: 1, HuffmanSpec.TheHuffmanSpecs[3]); - - var defaultLuminanceQuantTable = new JpegQuantizationTableConfig(0, Block8x8.Load(Quantization.LuminanceTable)); - var defaultChrominanceQuantTable = new JpegQuantizationTableConfig(1, Block8x8.Load(Quantization.ChrominanceTable)); - - var yCbCrHuffmanConfigs = new JpegHuffmanTableConfig[] - { - defaultLuminanceHuffmanDC, - defaultLuminanceHuffmanAC, - defaultChrominanceHuffmanDC, - defaultChrominanceHuffmanAC, - }; - - var yCbCrQuantTableConfigs = new JpegQuantizationTableConfig[] - { - defaultLuminanceQuantTable, - defaultChrominanceQuantTable, - }; - - return new JpegFrameConfig[] - { - // YCbCr 4:4:4 - new JpegFrameConfig( - JpegColorSpace.YCbCr, - JpegEncodingColor.YCbCrRatio444, - new JpegComponentConfig[] - { - new JpegComponentConfig(id: 1, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), - new JpegComponentConfig(id: 2, hsf: 1, vsf: 1, quantIndex: 1, dcIndex: 1, acIndex: 1), - new JpegComponentConfig(id: 3, hsf: 1, vsf: 1, quantIndex: 1, dcIndex: 1, acIndex: 1), - }, - yCbCrHuffmanConfigs, - yCbCrQuantTableConfigs), - - // YCbCr 4:2:2 - new JpegFrameConfig( - JpegColorSpace.YCbCr, - JpegEncodingColor.YCbCrRatio422, - new JpegComponentConfig[] - { - new JpegComponentConfig(id: 1, hsf: 2, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), - new JpegComponentConfig(id: 2, hsf: 1, vsf: 1, quantIndex: 1, dcIndex: 1, acIndex: 1), - new JpegComponentConfig(id: 3, hsf: 1, vsf: 1, quantIndex: 1, dcIndex: 1, acIndex: 1), - }, - yCbCrHuffmanConfigs, - yCbCrQuantTableConfigs), - - // YCbCr 4:2:0 - new JpegFrameConfig( - JpegColorSpace.YCbCr, - JpegEncodingColor.YCbCrRatio420, - new JpegComponentConfig[] - { - new JpegComponentConfig(id: 1, hsf: 2, vsf: 2, quantIndex: 0, dcIndex: 0, acIndex: 0), - new JpegComponentConfig(id: 2, hsf: 1, vsf: 1, quantIndex: 1, dcIndex: 1, acIndex: 1), - new JpegComponentConfig(id: 3, hsf: 1, vsf: 1, quantIndex: 1, dcIndex: 1, acIndex: 1), - }, - yCbCrHuffmanConfigs, - yCbCrQuantTableConfigs), - - // YCbCr 4:1:1 - new JpegFrameConfig( - JpegColorSpace.YCbCr, - JpegEncodingColor.YCbCrRatio411, - new JpegComponentConfig[] - { - new JpegComponentConfig(id: 1, hsf: 4, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), - new JpegComponentConfig(id: 2, hsf: 1, vsf: 1, quantIndex: 1, dcIndex: 1, acIndex: 1), - new JpegComponentConfig(id: 3, hsf: 1, vsf: 1, quantIndex: 1, dcIndex: 1, acIndex: 1), - }, - yCbCrHuffmanConfigs, - yCbCrQuantTableConfigs), - - // YCbCr 4:1:0 - new JpegFrameConfig( - JpegColorSpace.YCbCr, - JpegEncodingColor.YCbCrRatio410, - new JpegComponentConfig[] - { - new JpegComponentConfig(id: 1, hsf: 4, vsf: 2, quantIndex: 0, dcIndex: 0, acIndex: 0), - new JpegComponentConfig(id: 2, hsf: 1, vsf: 1, quantIndex: 1, dcIndex: 1, acIndex: 1), - new JpegComponentConfig(id: 3, hsf: 1, vsf: 1, quantIndex: 1, dcIndex: 1, acIndex: 1), - }, - yCbCrHuffmanConfigs, - yCbCrQuantTableConfigs), - - // Luminance - new JpegFrameConfig( - JpegColorSpace.Grayscale, - JpegEncodingColor.Luminance, - new JpegComponentConfig[] - { - new JpegComponentConfig(id: 0, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), - }, - new JpegHuffmanTableConfig[] - { - defaultLuminanceHuffmanDC, - defaultLuminanceHuffmanAC - }, - new JpegQuantizationTableConfig[] - { - defaultLuminanceQuantTable - }), - - // Rgb - new JpegFrameConfig( - JpegColorSpace.RGB, - JpegEncodingColor.Rgb, - new JpegComponentConfig[] - { - new JpegComponentConfig(id: 82, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), - new JpegComponentConfig(id: 71, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), - new JpegComponentConfig(id: 66, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), - }, - new JpegHuffmanTableConfig[] - { - defaultLuminanceHuffmanDC, - defaultLuminanceHuffmanAC - }, - new JpegQuantizationTableConfig[] - { - defaultLuminanceQuantTable - }), - - // Cmyk - new JpegFrameConfig( - JpegColorSpace.Cmyk, - JpegEncodingColor.Cmyk, - new JpegComponentConfig[] - { - new JpegComponentConfig(id: 1, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), - new JpegComponentConfig(id: 2, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), - new JpegComponentConfig(id: 3, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), - new JpegComponentConfig(id: 4, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), - }, - new JpegHuffmanTableConfig[] - { - defaultLuminanceHuffmanDC, - defaultLuminanceHuffmanAC - }, - new JpegQuantizationTableConfig[] - { - defaultLuminanceQuantTable - }), - }; - } - } - - internal class JpegFrameConfig - { - public JpegFrameConfig(JpegColorSpace colorType, JpegEncodingColor encodingColor, JpegComponentConfig[] components, JpegHuffmanTableConfig[] huffmanTables, JpegQuantizationTableConfig[] quantTables) - { - this.ColorType = colorType; - this.EncodingColor = encodingColor; - this.Components = components; - this.HuffmanTables = huffmanTables; - this.QuantizationTables = quantTables; - - this.MaxHorizontalSamplingFactor = components[0].HorizontalSampleFactor; - this.MaxVerticalSamplingFactor = components[0].VerticalSampleFactor; - for (int i = 1; i < components.Length; i++) - { - JpegComponentConfig component = components[i]; - this.MaxHorizontalSamplingFactor = Math.Max(this.MaxHorizontalSamplingFactor, component.HorizontalSampleFactor); - this.MaxVerticalSamplingFactor = Math.Max(this.MaxVerticalSamplingFactor, component.VerticalSampleFactor); - } - } - - public JpegColorSpace ColorType { get; } - - public JpegEncodingColor EncodingColor { get; } - - public JpegComponentConfig[] Components { get; } - - public JpegHuffmanTableConfig[] HuffmanTables { get; } - - public JpegQuantizationTableConfig[] QuantizationTables { get; } - - public int MaxHorizontalSamplingFactor { get; } - - public int MaxVerticalSamplingFactor { get; } - } - - internal class JpegComponentConfig - { - public JpegComponentConfig(byte id, int hsf, int vsf, int quantIndex, int dcIndex, int acIndex) - { - this.Id = id; - this.HorizontalSampleFactor = hsf; - this.VerticalSampleFactor = vsf; - this.QuantizatioTableIndex = quantIndex; - this.DcTableSelector = dcIndex; - this.AcTableSelector = acIndex; - } - - public byte Id { get; } - - public int HorizontalSampleFactor { get; } - - public int VerticalSampleFactor { get; } - - public int QuantizatioTableIndex { get; } - - public int DcTableSelector { get; } - - public int AcTableSelector { get; } - } - - internal class JpegHuffmanTableConfig - { - public JpegHuffmanTableConfig(int @class, int destIndex, HuffmanSpec table) - { - this.Class = @class; - this.DestinationIndex = destIndex; - this.Table = table; - } - - public int Class { get; } - - public int DestinationIndex { get; } - - public HuffmanSpec Table { get; } - } - - internal class JpegQuantizationTableConfig - { - public JpegQuantizationTableConfig(int destIndex, Block8x8 table) - { - this.DestinationIndex = destIndex; - this.Table = table; - } - - public int DestinationIndex { get; } - - public Block8x8 Table { get; } } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs index ae6c44f51f..fc13876213 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs @@ -24,27 +24,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg internal sealed unsafe class JpegEncoderCore : IImageEncoderInternals { /// - /// The number of quantization tables. + /// The available encodable frame configs. /// - private const int QuantizationTableCount = 2; + private static readonly JpegFrameConfig[] FrameConfigs = CreateFrameConfigs(); /// /// A scratch buffer to reduce allocations. /// private readonly byte[] buffer = new byte[20]; - /// - /// The quality, that will be used to encode the image. - /// - private readonly int? quality; - - private readonly bool? interleaved; - - private JpegEncodingColor? colorType; - - private JpegFrameConfig frameConfig; - - private HuffmanScanEncoder scanEncoder; + private readonly IJpegEncoderOptions options; /// /// The output stream. All attempted writes after the first error become no-ops. @@ -55,15 +44,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// Initializes a new instance of the class. /// /// The options. - /// Frame config. - public JpegEncoderCore(IJpegEncoderOptions options, JpegFrameConfig frameConfig) - { - this.quality = options.Quality; - this.interleaved = options.Interleaved; - - this.frameConfig = frameConfig; - this.colorType = frameConfig.EncodingColor; - } + public JpegEncoderCore(IJpegEncoderOptions options) + => this.options = options; public Block8x8F[] QuantizationTables { get; } = new Block8x8F[4]; @@ -87,68 +69,46 @@ namespace SixLabors.ImageSharp.Formats.Jpeg cancellationToken.ThrowIfCancellationRequested(); - var frame = new JpegFrame(this.frameConfig, Configuration.Default.MemoryAllocator, image, this.frameConfig.ColorType); - this.scanEncoder = new HuffmanScanEncoder(frame.BlocksPerMcu, stream); - this.outputStream = stream; + ImageMetadata metadata = image.Metadata; JpegMetadata jpegMetadata = metadata.GetJpegMetadata(); + JpegFrameConfig frameConfig = this.GetFrameConfig(jpegMetadata); + + bool interleaved = this.options.Interleaved ?? jpegMetadata.Interleaved ?? true; + using var frame = new JpegFrame(image, frameConfig, interleaved); // Write the Start Of Image marker. this.WriteStartOfImage(); - // Do not write APP0 marker for RGB colorspace. - if (this.colorType != JpegEncodingColor.Rgb) + // Write APP0 marker for any non-RGB colorspace image. + if (frameConfig.EncodingColor != JpegEncodingColor.Rgb) { this.WriteJfifApplicationHeader(metadata); } - // Write Exif, XMP, ICC and IPTC profiles - this.WriteProfiles(metadata); - - if (this.colorType == JpegEncodingColor.Rgb) + // Else write App14 marker to indicate RGB color space. + else { - // Write App14 marker to indicate RGB color space. this.WriteApp14Marker(); } + // Write Exif, XMP, ICC and IPTC profiles + this.WriteProfiles(metadata); + // Write the image dimensions. - this.WriteStartOfFrame(image.Width, image.Height, this.frameConfig); + this.WriteStartOfFrame(image.Width, image.Height, frameConfig); // Write the Huffman tables. - this.WriteDefineHuffmanTables(this.frameConfig.HuffmanTables); + var scanEncoder = new HuffmanScanEncoder(frame.BlocksPerMcu, stream); + this.WriteDefineHuffmanTables(frameConfig.HuffmanTables, scanEncoder); // Write the quantization tables. - this.WriteDefineQuantizationTables(this.frameConfig.QuantizationTables, jpegMetadata); + this.WriteDefineQuantizationTables(frameConfig.QuantizationTables, this.options.Quality, jpegMetadata); - var spectralConverter = new SpectralConverter(frame, image, this.QuantizationTables, Configuration.Default); - - if (frame.Components.Length == 1) - { - frame.AllocateComponents(fullScan: false); - - this.WriteStartOfScan(this.frameConfig.Components); - this.scanEncoder.EncodeScanBaselineSingleComponent(frame.Components[0], spectralConverter, cancellationToken); - } - else if (this.interleaved ?? jpegMetadata.Interleaved ?? true) - { - frame.AllocateComponents(fullScan: false); - - this.WriteStartOfScan(this.frameConfig.Components); - this.scanEncoder.EncodeScanBaselineInterleaved(this.frameConfig.EncodingColor, frame, spectralConverter, cancellationToken); - } - else - { - frame.AllocateComponents(fullScan: true); - spectralConverter.ConvertFull(); - - Span components = this.frameConfig.Components; - for (int i = 0; i < frame.Components.Length; i++) - { - this.WriteStartOfScan(components.Slice(i, 1)); - this.scanEncoder.EncodeScanBaseline(frame.Components[i], cancellationToken); - } - } + // Write scans with actual pixel data + using var spectralConverter = new SpectralConverter(frame, image, this.QuantizationTables); + this.WriteHuffmanScans(frame, frameConfig, spectralConverter, scanEncoder, cancellationToken); // Write the End Of Image marker. this.WriteEndOfImageMarker(); @@ -216,7 +176,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// /// Writes the Define Huffman Table marker and tables. /// - private void WriteDefineHuffmanTables(JpegHuffmanTableConfig[] tableConfigs) + private void WriteDefineHuffmanTables(JpegHuffmanTableConfig[] tableConfigs, HuffmanScanEncoder scanEncoder) { if (tableConfigs is null) { @@ -240,7 +200,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg this.outputStream.Write(tableConfig.Table.Count); this.outputStream.Write(tableConfig.Table.Values); - this.scanEncoder.BuildHuffmanTable(tableConfig); + scanEncoder.BuildHuffmanTable(tableConfig); } } @@ -629,6 +589,40 @@ namespace SixLabors.ImageSharp.Formats.Jpeg this.outputStream.Write(this.buffer, 0, 2); } + /// + /// Writes scans for given config. + /// + private void WriteHuffmanScans(JpegFrame frame, JpegFrameConfig frameConfig, SpectralConverter spectralConverter, HuffmanScanEncoder encoder, CancellationToken cancellationToken) + where TPixel : unmanaged, IPixel + { + if (frame.Components.Length == 1) + { + frame.AllocateComponents(fullScan: false); + + this.WriteStartOfScan(frameConfig.Components); + encoder.EncodeScanBaselineSingleComponent(frame.Components[0], spectralConverter, cancellationToken); + } + else if (frame.Interleaved) + { + frame.AllocateComponents(fullScan: false); + + this.WriteStartOfScan(frameConfig.Components); + encoder.EncodeScanBaselineInterleaved(frameConfig.EncodingColor, frame, spectralConverter, cancellationToken); + } + else + { + frame.AllocateComponents(fullScan: true); + spectralConverter.ConvertFull(); + + Span components = frameConfig.Components; + for (int i = 0; i < frame.Components.Length; i++) + { + this.WriteStartOfScan(components.Slice(i, 1)); + encoder.EncodeScanBaseline(frame.Components[i], cancellationToken); + } + } + } + /// /// Writes the header for a marker with the given length. /// @@ -656,8 +650,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// /// /// Quantization tables configs. + /// Optional quality value from the options. /// Jpeg metadata instance. - private void WriteDefineQuantizationTables(JpegQuantizationTableConfig[] configs, JpegMetadata metadata) + private void WriteDefineQuantizationTables(JpegQuantizationTableConfig[] configs, int? optionsQuality, JpegMetadata metadata) { int dataLen = configs.Length * (1 + Block8x8.Size); @@ -668,11 +663,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg byte[] buffer = new byte[dataLen]; int offset = 0; + Block8x8F workspaceBlock = default; + for (int i = 0; i < configs.Length; i++) { JpegQuantizationTableConfig config = configs[i]; - int quality = GetQualityForTable(config.DestinationIndex, this.quality, metadata); + int quality = GetQualityForTable(config.DestinationIndex, optionsQuality, metadata); Block8x8 scaledTable = Quantization.ScaleQuantizationTable(quality, config.Table); // write to the output stream @@ -683,8 +680,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg buffer[offset++] = (byte)(uint)scaledTable[ZigZag.ZigZagOrder[j]]; } - // apply scaling and save into buffer - this.QuantizationTables[config.DestinationIndex].LoadFromInt16Scalar(ref scaledTable); + // apply FDCT multipliers and inject to the destination index + workspaceBlock.LoadFrom(ref scaledTable); + FastFloatingPointDCT.AdjustToFDCT(ref workspaceBlock); + + this.QuantizationTables[config.DestinationIndex] = workspaceBlock; } // write filled buffer to the stream @@ -692,10 +692,177 @@ namespace SixLabors.ImageSharp.Formats.Jpeg static int GetQualityForTable(int destIndex, int? encoderQuality, JpegMetadata metadata) => destIndex switch { - 0 => encoderQuality ?? metadata.LuminanceQuality, - 1 => encoderQuality ?? metadata.ChrominanceQuality, + 0 => encoderQuality ?? metadata.LuminanceQuality ?? Quantization.DefaultQualityFactor, + 1 => encoderQuality ?? metadata.ChrominanceQuality ?? Quantization.DefaultQualityFactor, _ => encoderQuality ?? metadata.Quality, }; } + + private JpegFrameConfig GetFrameConfig(JpegMetadata metadata) + { + JpegEncodingColor color = this.options.ColorType ?? metadata.ColorType ?? JpegEncodingColor.YCbCrRatio420; + JpegFrameConfig frameConfig = Array.Find( + FrameConfigs, + cfg => cfg.EncodingColor == color); + + if (frameConfig == null) + { + throw new ArgumentException(nameof(color)); + } + + return frameConfig; + } + + private static JpegFrameConfig[] CreateFrameConfigs() + { + var defaultLuminanceHuffmanDC = new JpegHuffmanTableConfig(@class: 0, destIndex: 0, HuffmanSpec.TheHuffmanSpecs[0]); + var defaultLuminanceHuffmanAC = new JpegHuffmanTableConfig(@class: 1, destIndex: 0, HuffmanSpec.TheHuffmanSpecs[1]); + var defaultChrominanceHuffmanDC = new JpegHuffmanTableConfig(@class: 0, destIndex: 1, HuffmanSpec.TheHuffmanSpecs[2]); + var defaultChrominanceHuffmanAC = new JpegHuffmanTableConfig(@class: 1, destIndex: 1, HuffmanSpec.TheHuffmanSpecs[3]); + + var defaultLuminanceQuantTable = new JpegQuantizationTableConfig(0, Quantization.LuminanceTable); + var defaultChrominanceQuantTable = new JpegQuantizationTableConfig(1, Quantization.ChrominanceTable); + + var yCbCrHuffmanConfigs = new JpegHuffmanTableConfig[] + { + defaultLuminanceHuffmanDC, + defaultLuminanceHuffmanAC, + defaultChrominanceHuffmanDC, + defaultChrominanceHuffmanAC, + }; + + var yCbCrQuantTableConfigs = new JpegQuantizationTableConfig[] + { + defaultLuminanceQuantTable, + defaultChrominanceQuantTable, + }; + + return new JpegFrameConfig[] + { + // YCbCr 4:4:4 + new JpegFrameConfig( + JpegColorSpace.YCbCr, + JpegEncodingColor.YCbCrRatio444, + new JpegComponentConfig[] + { + new JpegComponentConfig(id: 1, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), + new JpegComponentConfig(id: 2, hsf: 1, vsf: 1, quantIndex: 1, dcIndex: 1, acIndex: 1), + new JpegComponentConfig(id: 3, hsf: 1, vsf: 1, quantIndex: 1, dcIndex: 1, acIndex: 1), + }, + yCbCrHuffmanConfigs, + yCbCrQuantTableConfigs), + + // YCbCr 4:2:2 + new JpegFrameConfig( + JpegColorSpace.YCbCr, + JpegEncodingColor.YCbCrRatio422, + new JpegComponentConfig[] + { + new JpegComponentConfig(id: 1, hsf: 2, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), + new JpegComponentConfig(id: 2, hsf: 1, vsf: 1, quantIndex: 1, dcIndex: 1, acIndex: 1), + new JpegComponentConfig(id: 3, hsf: 1, vsf: 1, quantIndex: 1, dcIndex: 1, acIndex: 1), + }, + yCbCrHuffmanConfigs, + yCbCrQuantTableConfigs), + + // YCbCr 4:2:0 + new JpegFrameConfig( + JpegColorSpace.YCbCr, + JpegEncodingColor.YCbCrRatio420, + new JpegComponentConfig[] + { + new JpegComponentConfig(id: 1, hsf: 2, vsf: 2, quantIndex: 0, dcIndex: 0, acIndex: 0), + new JpegComponentConfig(id: 2, hsf: 1, vsf: 1, quantIndex: 1, dcIndex: 1, acIndex: 1), + new JpegComponentConfig(id: 3, hsf: 1, vsf: 1, quantIndex: 1, dcIndex: 1, acIndex: 1), + }, + yCbCrHuffmanConfigs, + yCbCrQuantTableConfigs), + + // YCbCr 4:1:1 + new JpegFrameConfig( + JpegColorSpace.YCbCr, + JpegEncodingColor.YCbCrRatio411, + new JpegComponentConfig[] + { + new JpegComponentConfig(id: 1, hsf: 4, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), + new JpegComponentConfig(id: 2, hsf: 1, vsf: 1, quantIndex: 1, dcIndex: 1, acIndex: 1), + new JpegComponentConfig(id: 3, hsf: 1, vsf: 1, quantIndex: 1, dcIndex: 1, acIndex: 1), + }, + yCbCrHuffmanConfigs, + yCbCrQuantTableConfigs), + + // YCbCr 4:1:0 + new JpegFrameConfig( + JpegColorSpace.YCbCr, + JpegEncodingColor.YCbCrRatio410, + new JpegComponentConfig[] + { + new JpegComponentConfig(id: 1, hsf: 4, vsf: 2, quantIndex: 0, dcIndex: 0, acIndex: 0), + new JpegComponentConfig(id: 2, hsf: 1, vsf: 1, quantIndex: 1, dcIndex: 1, acIndex: 1), + new JpegComponentConfig(id: 3, hsf: 1, vsf: 1, quantIndex: 1, dcIndex: 1, acIndex: 1), + }, + yCbCrHuffmanConfigs, + yCbCrQuantTableConfigs), + + // Luminance + new JpegFrameConfig( + JpegColorSpace.Grayscale, + JpegEncodingColor.Luminance, + new JpegComponentConfig[] + { + new JpegComponentConfig(id: 0, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), + }, + new JpegHuffmanTableConfig[] + { + defaultLuminanceHuffmanDC, + defaultLuminanceHuffmanAC + }, + new JpegQuantizationTableConfig[] + { + defaultLuminanceQuantTable + }), + + // Rgb + new JpegFrameConfig( + JpegColorSpace.RGB, + JpegEncodingColor.Rgb, + new JpegComponentConfig[] + { + new JpegComponentConfig(id: 82, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), + new JpegComponentConfig(id: 71, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), + new JpegComponentConfig(id: 66, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), + }, + new JpegHuffmanTableConfig[] + { + defaultLuminanceHuffmanDC, + defaultLuminanceHuffmanAC + }, + new JpegQuantizationTableConfig[] + { + defaultLuminanceQuantTable + }), + + // Cmyk + new JpegFrameConfig( + JpegColorSpace.Cmyk, + JpegEncodingColor.Cmyk, + new JpegComponentConfig[] + { + new JpegComponentConfig(id: 1, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), + new JpegComponentConfig(id: 2, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), + new JpegComponentConfig(id: 3, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), + new JpegComponentConfig(id: 4, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), + }, + new JpegHuffmanTableConfig[] + { + defaultLuminanceHuffmanDC, + defaultLuminanceHuffmanAC + }, + new JpegQuantizationTableConfig[] + { + defaultLuminanceQuantTable + }), + }; + } } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs b/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs index b878d26fb8..ef253cfebc 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs @@ -11,16 +11,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// public class JpegMetadata : IDeepCloneable { - /// - /// Backing field for - /// - private int? luminanceQuality; - - /// - /// Backing field for - /// - private int? chrominanceQuality; - /// /// Initializes a new instance of the class. /// @@ -36,8 +26,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg { this.ColorType = other.ColorType; - this.luminanceQuality = other.luminanceQuality; - this.chrominanceQuality = other.chrominanceQuality; + this.LuminanceQuality = other.LuminanceQuality; + this.ChrominanceQuality = other.ChrominanceQuality; } /// @@ -47,11 +37,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// This value might not be accurate if it was calculated during jpeg decoding /// with non-complient ITU quantization tables. /// - internal int LuminanceQuality - { - get => this.luminanceQuality ?? Quantization.DefaultQualityFactor; - set => this.luminanceQuality = value; - } + internal int? LuminanceQuality { get; set; } /// /// Gets or sets the jpeg chrominance quality. @@ -60,11 +46,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// This value might not be accurate if it was calculated during jpeg decoding /// with non-complient ITU quantization tables. /// - internal int ChrominanceQuality - { - get => this.chrominanceQuality ?? Quantization.DefaultQualityFactor; - set => this.chrominanceQuality = value; - } + internal int? ChrominanceQuality { get; set; } /// /// Gets the encoded quality. @@ -77,20 +59,20 @@ namespace SixLabors.ImageSharp.Formats.Jpeg { get { - if (this.luminanceQuality.HasValue) + if (this.LuminanceQuality.HasValue) { - if (this.chrominanceQuality.HasValue) + if (this.ChrominanceQuality.HasValue) { - return Math.Max(this.luminanceQuality.Value, this.chrominanceQuality.Value); + return Math.Max(this.LuminanceQuality.Value, this.ChrominanceQuality.Value); } - return this.luminanceQuality.Value; + return this.LuminanceQuality.Value; } else { - if (this.chrominanceQuality.HasValue) + if (this.ChrominanceQuality.HasValue) { - return this.chrominanceQuality.Value; + return this.ChrominanceQuality.Value; } return Quantization.DefaultQualityFactor; diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgb24.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgb24.PixelOperations.cs index 5f8a3e95f5..3e96c9fb46 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgb24.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgb24.PixelOperations.cs @@ -39,12 +39,11 @@ namespace SixLabors.ImageSharp.PixelFormats /// internal override void UnpackIntoRgbPlanes( - Configuration configuration, - ReadOnlySpan redChannel, - ReadOnlySpan greenChannel, - ReadOnlySpan blueChannel, - Span source) - => SimdUtils.UnpackToRgbPlanes(redChannel, greenChannel, blueChannel, source); + Span redChannel, + Span greenChannel, + Span blueChannel, + ReadOnlySpan source) + => SimdUtils.UnpackToRgbPlanes(redChannel, greenChannel, blueChannel, source); } } } diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs index dcc3617957..e753f24aa3 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs @@ -202,32 +202,27 @@ namespace SixLabors.ImageSharp.PixelFormats /// Bulk operation that unpacks pixels from /// into 3 seperate RGB channels. The destination must have a padding of 3. /// - /// A to configure internal operations. /// A to the red values. /// A to the green values. /// A to the blue values. /// A to the destination pixels. internal virtual void UnpackIntoRgbPlanes( - Configuration configuration, - ReadOnlySpan redChannel, - ReadOnlySpan greenChannel, - ReadOnlySpan blueChannel, - Span source) + Span redChannel, + Span greenChannel, + Span blueChannel, + ReadOnlySpan source) { - Guard.NotNull(configuration, nameof(configuration)); - int count = redChannel.Length; Rgba32 rgba32 = default; + ref float r = ref MemoryMarshal.GetReference(redChannel); ref float g = ref MemoryMarshal.GetReference(greenChannel); ref float b = ref MemoryMarshal.GetReference(blueChannel); - ref TPixel d = ref MemoryMarshal.GetReference(source); - + ref TPixel src = ref MemoryMarshal.GetReference(source); for (int i = 0; i < count; i++) { - // TODO: Create ToRgb24 method in IPixel - Unsafe.Add(ref d, i).ToRgba32(ref rgba32); + Unsafe.Add(ref src, i).ToRgba32(ref rgba32); Unsafe.Add(ref r, i) = rgba32.R; Unsafe.Add(ref g, i) = rgba32.G; Unsafe.Add(ref b, i) = rgba32.B; diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/CmykColorConversion.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/CmykColorConversion.cs index 11125357ca..9d1e5c13f1 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/CmykColorConversion.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/CmykColorConversion.cs @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg { var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - new JpegColorConverterBase.FromCmykScalar(8).ConvertToRgbInplace(values); + new JpegColorConverterBase.CmykScalar(8).ConvertToRgbInplace(values); } [Benchmark] @@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg { var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - new JpegColorConverterBase.FromCmykVector(8).ConvertToRgbInplace(values); + new JpegColorConverterBase.CmykVector(8).ConvertToRgbInplace(values); } #if SUPPORTS_RUNTIME_INTRINSICS @@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg { var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - new JpegColorConverterBase.FromCmykAvx(8).ConvertToRgbInplace(values); + new JpegColorConverterBase.CmykAvx(8).ConvertToRgbInplace(values); } #endif } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/GrayscaleColorConversion.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/GrayscaleColorConversion.cs index 48e6332eed..ef20570d2f 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/GrayscaleColorConversion.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/GrayscaleColorConversion.cs @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg { var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - new JpegColorConverterBase.FromGrayscaleScalar(8).ConvertToRgbInplace(values); + new JpegColorConverterBase.GrayscaleScalar(8).ConvertToRgbInplace(values); } #if SUPPORTS_RUNTIME_INTRINSICS @@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg { var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - new JpegColorConverterBase.FromGrayscaleAvx(8).ConvertToRgbInplace(values); + new JpegColorConverterBase.GrayscaleAvx(8).ConvertToRgbInplace(values); } #endif } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/RgbColorConversion.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/RgbColorConversion.cs index 438626b4b6..225ecbba9e 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/RgbColorConversion.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/RgbColorConversion.cs @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg { var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - new JpegColorConverterBase.FromRgbScalar(8).ConvertToRgbInplace(values); + new JpegColorConverterBase.RgbScalar(8).ConvertToRgbInplace(values); } [Benchmark] @@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg { var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - new JpegColorConverterBase.FromRgbVector(8).ConvertToRgbInplace(values); + new JpegColorConverterBase.RgbVector(8).ConvertToRgbInplace(values); } #if SUPPORTS_RUNTIME_INTRINSICS @@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg { var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - new JpegColorConverterBase.FromRgbAvx(8).ConvertToRgbInplace(values); + new JpegColorConverterBase.RgbAvx(8).ConvertToRgbInplace(values); } #endif } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YCbCrColorConversion.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YCbCrColorConversion.cs index 6f65f1b853..b5113797ec 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YCbCrColorConversion.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YCbCrColorConversion.cs @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg { var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - new JpegColorConverterBase.FromYCbCrScalar(8).ConvertToRgbInplace(values); + new JpegColorConverterBase.YCbCrScalar(8).ConvertToRgbInplace(values); } [Benchmark] @@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg { var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - new JpegColorConverterBase.FromYCbCrVector(8).ConvertToRgbInplace(values); + new JpegColorConverterBase.YCbCrVector(8).ConvertToRgbInplace(values); } #if SUPPORTS_RUNTIME_INTRINSICS @@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg { var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - new JpegColorConverterBase.FromYCbCrAvx(8).ConvertToRgbInplace(values); + new JpegColorConverterBase.YCbCrAvx(8).ConvertToRgbInplace(values); } #endif } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YccKColorConverter.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YccKColorConverter.cs index d03fa5e83e..1b73b26a10 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YccKColorConverter.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YccKColorConverter.cs @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg { var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - new JpegColorConverterBase.FromYccKScalar(8).ConvertToRgbInplace(values); + new JpegColorConverterBase.YccKScalar(8).ConvertToRgbInplace(values); } [Benchmark] @@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg { var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - new JpegColorConverterBase.FromYccKVector(8).ConvertToRgbInplace(values); + new JpegColorConverterBase.YccKVector(8).ConvertToRgbInplace(values); } #if SUPPORTS_RUNTIME_INTRINSICS @@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg { var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - new JpegColorConverterBase.FromYccKAvx(8).ConvertToRgbInplace(values); + new JpegColorConverterBase.YccKAvx(8).ConvertToRgbInplace(values); } #endif } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegFeatures.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegFeatures.cs index b115550f93..6844883486 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegFeatures.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegFeatures.cs @@ -22,8 +22,13 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg // No metadata private const string TestImage = TestImages.Jpeg.Baseline.Calliphora; - public static IEnumerable ColorSpaceValues => - new[] { JpegEncodingColor.Luminance, JpegEncodingColor.Rgb, JpegEncodingColor.YCbCrRatio420, JpegEncodingColor.YCbCrRatio444 }; + public static IEnumerable ColorSpaceValues => new[] + { + JpegEncodingColor.Luminance, + JpegEncodingColor.Rgb, + JpegEncodingColor.YCbCrRatio420, + JpegEncodingColor.YCbCrRatio444, + }; [Params(75, 90, 100)] public int Quality; @@ -41,7 +46,12 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg { using FileStream imageBinaryStream = File.OpenRead(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImage)); this.bmpCore = Image.Load(imageBinaryStream); - this.encoder = new JpegEncoder { Quality = this.Quality, ColorType = this.TargetColorSpace }; + this.encoder = new JpegEncoder + { + Quality = this.Quality, + ColorType = this.TargetColorSpace, + Interleaved = true, + }; this.destinationStream = new MemoryStream(); } @@ -67,23 +77,23 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg /* BenchmarkDotNet=v0.13.0, OS=Windows 10.0.19044 Intel Core i7-6700K CPU 4.00GHz (Skylake), 1 CPU, 8 logical and 4 physical cores -.NET SDK=6.0.100-preview.3.21202.5 - [Host] : .NET Core 3.1.21 (CoreCLR 4.700.21.51404, CoreFX 4.700.21.51508), X64 RyuJIT - DefaultJob : .NET Core 3.1.21 (CoreCLR 4.700.21.51404, CoreFX 4.700.21.51508), X64 RyuJIT +.NET SDK=6.0.202 + [Host] : .NET 6.0.4 (6.0.422.16404), X64 RyuJIT + DefaultJob : .NET 6.0.4 (6.0.422.16404), X64 RyuJIT | Method | TargetColorSpace | Quality | Mean | Error | StdDev | |---------- |----------------- |-------- |----------:|----------:|----------:| -| Benchmark | Luminance | 75 | 7.055 ms | 0.1411 ms | 0.3297 ms | -| Benchmark | Rgb | 75 | 12.139 ms | 0.0645 ms | 0.0538 ms | -| Benchmark | YCbCrRatio420 | 75 | 6.463 ms | 0.0282 ms | 0.0235 ms | -| Benchmark | YCbCrRatio444 | 75 | 8.616 ms | 0.0422 ms | 0.0374 ms | -| Benchmark | Luminance | 90 | 7.011 ms | 0.0361 ms | 0.0301 ms | -| Benchmark | Rgb | 90 | 13.119 ms | 0.0947 ms | 0.0886 ms | -| Benchmark | YCbCrRatio420 | 90 | 6.786 ms | 0.0328 ms | 0.0274 ms | -| Benchmark | YCbCrRatio444 | 90 | 8.672 ms | 0.0772 ms | 0.0722 ms | -| Benchmark | Luminance | 100 | 9.554 ms | 0.1211 ms | 0.1012 ms | -| Benchmark | Rgb | 100 | 19.475 ms | 0.1080 ms | 0.0958 ms | -| Benchmark | YCbCrRatio420 | 100 | 10.146 ms | 0.0585 ms | 0.0519 ms | -| Benchmark | YCbCrRatio444 | 100 | 15.317 ms | 0.0709 ms | 0.0592 ms | +| Benchmark | Luminance | 75 | 4.575 ms | 0.0233 ms | 0.0207 ms | +| Benchmark | Rgb | 75 | 12.477 ms | 0.1051 ms | 0.0932 ms | +| Benchmark | YCbCrRatio420 | 75 | 6.421 ms | 0.0464 ms | 0.0434 ms | +| Benchmark | YCbCrRatio444 | 75 | 8.449 ms | 0.1246 ms | 0.1166 ms | +| Benchmark | Luminance | 90 | 4.863 ms | 0.0120 ms | 0.0106 ms | +| Benchmark | Rgb | 90 | 13.287 ms | 0.0548 ms | 0.0513 ms | +| Benchmark | YCbCrRatio420 | 90 | 7.012 ms | 0.0533 ms | 0.0499 ms | +| Benchmark | YCbCrRatio444 | 90 | 8.916 ms | 0.1285 ms | 0.1202 ms | +| Benchmark | Luminance | 100 | 6.665 ms | 0.0136 ms | 0.0113 ms | +| Benchmark | Rgb | 100 | 19.734 ms | 0.0477 ms | 0.0446 ms | +| Benchmark | YCbCrRatio420 | 100 | 10.541 ms | 0.0925 ms | 0.0865 ms | +| Benchmark | YCbCrRatio444 | 100 | 15.587 ms | 0.1695 ms | 0.1586 ms | */ diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs index 4db718e12a..3536a05171 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs @@ -22,11 +22,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg private const int TestBufferLength = 40; -#if SUPPORTS_RUNTIME_INTRINSICS private static readonly HwIntrinsics IntrinsicsConfig = HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX; -#else - private static readonly HwIntrinsics IntrinsicsConfig = HwIntrinsics.AllowAll; -#endif private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new(epsilon: Precision); @@ -52,7 +48,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg public void GetConverterThrowsExceptionOnInvalidPrecision() { // Valid precisions: 8 & 12 bit - Assert.Throws(() => JpegColorConverterBase.GetConverter(JpegColorSpace.YCbCr, 9)); + int invalidPrecision = 9; + Assert.Throws(() => JpegColorConverterBase.GetConverter(JpegColorSpace.YCbCr, invalidPrecision)); } [Theory] @@ -94,13 +91,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [Theory] [MemberData(nameof(Seeds))] public void FromYCbCrBasic(int seed) => - this.TestConverter(new JpegColorConverterBase.FromYCbCrScalar(8), 3, seed); + this.TestConverter(new JpegColorConverterBase.YCbCrScalar(8), 3, seed); [Theory] [MemberData(nameof(Seeds))] public void FromYCbCrVector(int seed) { - var converter = new JpegColorConverterBase.FromYCbCrVector(8); + var converter = new JpegColorConverterBase.YCbCrVector(8); if (!converter.IsAvailable) { @@ -116,7 +113,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg static void RunTest(string arg) => ValidateConversion( - new JpegColorConverterBase.FromYCbCrVector(8), + new JpegColorConverterBase.YCbCrVector(8), 3, FeatureTestRunner.Deserialize(arg)); } @@ -124,13 +121,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [Theory] [MemberData(nameof(Seeds))] public void FromCmykBasic(int seed) => - this.TestConverter(new JpegColorConverterBase.FromCmykScalar(8), 4, seed); + this.TestConverter(new JpegColorConverterBase.CmykScalar(8), 4, seed); [Theory] [MemberData(nameof(Seeds))] public void FromCmykVector(int seed) { - var converter = new JpegColorConverterBase.FromCmykVector(8); + var converter = new JpegColorConverterBase.CmykVector(8); if (!converter.IsAvailable) { @@ -146,7 +143,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg static void RunTest(string arg) => ValidateConversion( - new JpegColorConverterBase.FromCmykVector(8), + new JpegColorConverterBase.CmykVector(8), 4, FeatureTestRunner.Deserialize(arg)); } @@ -154,13 +151,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [Theory] [MemberData(nameof(Seeds))] public void FromGrayscaleBasic(int seed) => - this.TestConverter(new JpegColorConverterBase.FromGrayscaleScalar(8), 1, seed); + this.TestConverter(new JpegColorConverterBase.GrayscaleScalar(8), 1, seed); [Theory] [MemberData(nameof(Seeds))] public void FromGrayscaleVector(int seed) { - var converter = new JpegColorConverterBase.FromGrayScaleVector(8); + var converter = new JpegColorConverterBase.GrayScaleVector(8); if (!converter.IsAvailable) { @@ -176,7 +173,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg static void RunTest(string arg) => ValidateConversion( - new JpegColorConverterBase.FromGrayScaleVector(8), + new JpegColorConverterBase.GrayScaleVector(8), 1, FeatureTestRunner.Deserialize(arg)); } @@ -184,13 +181,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [Theory] [MemberData(nameof(Seeds))] public void FromRgbBasic(int seed) => - this.TestConverter(new JpegColorConverterBase.FromRgbScalar(8), 3, seed); + this.TestConverter(new JpegColorConverterBase.RgbScalar(8), 3, seed); [Theory] [MemberData(nameof(Seeds))] public void FromRgbVector(int seed) { - var converter = new JpegColorConverterBase.FromRgbVector(8); + var converter = new JpegColorConverterBase.RgbVector(8); if (!converter.IsAvailable) { @@ -206,7 +203,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg static void RunTest(string arg) => ValidateConversion( - new JpegColorConverterBase.FromRgbVector(8), + new JpegColorConverterBase.RgbVector(8), 3, FeatureTestRunner.Deserialize(arg)); } @@ -214,13 +211,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [Theory] [MemberData(nameof(Seeds))] public void FromYccKBasic(int seed) => - this.TestConverter(new JpegColorConverterBase.FromYccKScalar(8), 4, seed); + this.TestConverter(new JpegColorConverterBase.YccKScalar(8), 4, seed); [Theory] [MemberData(nameof(Seeds))] public void FromYccKVector(int seed) { - var converter = new JpegColorConverterBase.FromYccKVector(8); + var converter = new JpegColorConverterBase.YccKVector(8); if (!converter.IsAvailable) { @@ -236,37 +233,35 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg static void RunTest(string arg) => ValidateConversion( - new JpegColorConverterBase.FromYccKVector(8), + new JpegColorConverterBase.YccKVector(8), 4, FeatureTestRunner.Deserialize(arg)); } -#if SUPPORTS_RUNTIME_INTRINSICS [Theory] [MemberData(nameof(Seeds))] public void FromYCbCrAvx2(int seed) => - this.TestConverter(new JpegColorConverterBase.FromYCbCrAvx(8), 3, seed); + this.TestConverter(new JpegColorConverterBase.YCbCrAvx(8), 3, seed); [Theory] [MemberData(nameof(Seeds))] public void FromCmykAvx2(int seed) => - this.TestConverter(new JpegColorConverterBase.FromCmykAvx(8), 4, seed); + this.TestConverter(new JpegColorConverterBase.CmykAvx(8), 4, seed); [Theory] [MemberData(nameof(Seeds))] public void FromGrayscaleAvx2(int seed) => - this.TestConverter(new JpegColorConverterBase.FromGrayscaleAvx(8), 1, seed); + this.TestConverter(new JpegColorConverterBase.GrayscaleAvx(8), 1, seed); [Theory] [MemberData(nameof(Seeds))] public void FromRgbAvx2(int seed) => - this.TestConverter(new JpegColorConverterBase.FromRgbAvx(8), 3, seed); + this.TestConverter(new JpegColorConverterBase.RgbAvx(8), 3, seed); [Theory] [MemberData(nameof(Seeds))] public void FromYccKAvx2(int seed) => - this.TestConverter(new JpegColorConverterBase.FromYccKAvx(8), 4, seed); -#endif + this.TestConverter(new JpegColorConverterBase.YccKAvx(8), 4, seed); private void TestConverter( JpegColorConverterBase converter, diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs index 7a5dcedef9..e44c75927d 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs @@ -145,7 +145,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [InlineData(TestImages.Jpeg.Baseline.JpegRgb, JpegEncodingColor.Rgb)] [InlineData(TestImages.Jpeg.Baseline.Cmyk, JpegEncodingColor.Cmyk)] [InlineData(TestImages.Jpeg.Baseline.Jpeg410, JpegEncodingColor.YCbCrRatio410)] - [InlineData(TestImages.Jpeg.Baseline.Jpeg422, JpegEncodingColor.YCbCrRatio422)] [InlineData(TestImages.Jpeg.Baseline.Jpeg411, JpegEncodingColor.YCbCrRatio411)] public void Identify_DetectsCorrectColorType(string imagePath, JpegEncodingColor expectedColorType) { diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs index 9f36fcc0f1..cf61e57830 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs @@ -85,53 +85,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg Assert.Equal(expectedColorType, meta.ColorType); } - [Theory] - [WithFile(TestImages.Jpeg.Baseline.Cmyk, PixelTypes.Rgba32)] - [WithFile(TestImages.Jpeg.Baseline.Jpeg410, PixelTypes.Rgba32)] - [WithFile(TestImages.Jpeg.Baseline.Jpeg411, PixelTypes.Rgba32)] - [WithFile(TestImages.Jpeg.Baseline.Jpeg422, PixelTypes.Rgba32)] - public void Encode_WithUnsupportedColorType_FromInputImage_DefaultsToYCbCr420(TestImageProvider provider) - where TPixel : unmanaged, IPixel - { - // arrange - using Image input = provider.GetImage(JpegDecoder); - using var memoryStream = new MemoryStream(); - - // act - input.Save(memoryStream, new JpegEncoder() - { - Quality = 75 - }); - - // assert - memoryStream.Position = 0; - using var output = Image.Load(memoryStream); - JpegMetadata meta = output.Metadata.GetJpegMetadata(); - Assert.Equal(JpegEncodingColor.YCbCrRatio420, meta.ColorType); - } - - [Theory] - [InlineData(JpegEncodingColor.Cmyk)] - [InlineData(JpegEncodingColor.YCbCrRatio410)] - [InlineData(JpegEncodingColor.YCbCrRatio411)] - [InlineData(JpegEncodingColor.YCbCrRatio422)] - public void Encode_WithUnsupportedColorType_DefaultsToYCbCr420(JpegEncodingColor colorType) - { - // arrange - var jpegEncoder = new JpegEncoder() { ColorType = colorType }; - using var input = new Image(10, 10); - using var memoryStream = new MemoryStream(); - - // act - input.Save(memoryStream, jpegEncoder); - - // assert - memoryStream.Position = 0; - using var output = Image.Load(memoryStream); - JpegMetadata meta = output.Metadata.GetJpegMetadata(); - Assert.Equal(JpegEncodingColor.YCbCrRatio420, meta.ColorType); - } - [Theory] [MemberData(nameof(QualityFiles))] public void Encode_PreservesQuality(string imagePath, int quality) @@ -178,7 +131,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [WithTestPatternImages(nameof(BitsPerPixel_Quality), 46, 8, PixelTypes.Rgba32)] [WithTestPatternImages(nameof(BitsPerPixel_Quality), 51, 7, PixelTypes.Rgba32)] public void EncodeBaseline_WithSmallImages_WorksWithDifferentSizes(TestImageProvider provider, JpegEncodingColor colorType, int quality) - where TPixel : unmanaged, IPixel => TestJpegEncoderCore(provider, colorType, quality, comparer: ImageComparer.Tolerant(0.12f)); + where TPixel : unmanaged, IPixel => TestJpegEncoderCore(provider, colorType, quality, comparer: ImageComparer.Tolerant(0.15f)); [Theory] [WithFile(TestImages.Png.BikeGrayscale, nameof(Grayscale_Quality), PixelTypes.L8)] @@ -265,34 +218,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg } [Fact] - public void Quality_0_And_1_Are_Identical() - { - var options = new JpegEncoder - { - Quality = 0 - }; - - var testFile = TestFile.Create(TestImages.Jpeg.Baseline.Calliphora); - - using (Image input = testFile.CreateRgba32Image()) - using (var memStream0 = new MemoryStream()) - using (var memStream1 = new MemoryStream()) - { - input.SaveAsJpeg(memStream0, options); - - options.Quality = 1; - input.SaveAsJpeg(memStream1, options); - - Assert.Equal(memStream0.ToArray(), memStream1.ToArray()); - } - } - - [Fact] - public void Quality_0_And_100_Are_Not_Identical() + public void Quality_1_And_100_Are_Not_Identical() { var options = new JpegEncoder { - Quality = 0 + Quality = 1 }; var testFile = TestFile.Create(TestImages.Jpeg.Baseline.Calliphora);