From 51ed7c7191aa895afa4ddeada459fcff8f6d292f Mon Sep 17 00:00:00 2001 From: Stephan Vedder Date: Thu, 6 Dec 2018 23:40:20 +0100 Subject: [PATCH 001/223] Start work on jpeg 12 bit support --- src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs | 10 +++++----- .../Formats/Jpg/JpegDecoderTests.Images.cs | 5 ++++- tests/ImageSharp.Tests/TestImages.cs | 4 +++- tests/Images/Input/Jpg/baseline/testorig12.jpg | 3 +++ 4 files changed, 15 insertions(+), 7 deletions(-) create mode 100644 tests/Images/Input/Jpg/baseline/testorig12.jpg diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index f6da9cb2ec..5bfe88cc63 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// /// The only supported precision /// - public const int SupportedPrecision = 8; + public readonly int[] SupportedPrecisions = { 8, 12 }; /// /// The global configuration @@ -137,7 +137,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// /// Gets the color depth, in number of bits per pixel. /// - public int BitsPerPixel => this.ComponentCount * SupportedPrecision; + public int BitsPerPixel => this.ComponentCount * this.Frame.Precision; /// /// Gets the input stream. @@ -720,10 +720,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg this.InputStream.Read(this.temp, 0, remaining); - // We only support 8-bit precision. - if (this.temp[0] != SupportedPrecision) + // We only support 8-bit and 12-bit precision. + if (!SupportedPrecisions.Contains(this.temp[0])) { - throw new ImageFormatException("Only 8-Bit precision supported."); + throw new ImageFormatException("Only 8-Bit and 12-Bit precision supported."); } this.Frame = new JpegFrame diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs index 40de25b30a..03f1826edd 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs @@ -28,7 +28,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg TestImages.Jpeg.Issues.ExifResizeOutOfRange696, TestImages.Jpeg.Issues.InvalidAPP0721, TestImages.Jpeg.Issues.ExifGetString750Load, - TestImages.Jpeg.Issues.ExifGetString750Transform + TestImages.Jpeg.Issues.ExifGetString750Transform, + + // High depth images + TestImages.Jpeg.Baseline.Testorig12bit, }; public static string[] ProgressiveTestJpegs = diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 1144a3f7c0..46f9459c52 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -135,12 +135,14 @@ namespace SixLabors.ImageSharp.Tests public const string Testorig420 = "Jpg/baseline/testorig.jpg"; public const string MultiScanBaselineCMYK = "Jpg/baseline/MultiScanBaselineCMYK.jpg"; public const string Ratio1x1 = "Jpg/baseline/ratio-1x1.jpg"; + public const string Testorig12bit = "Jpg/baseline/testorig12.jpg"; public static readonly string[] All = { Cmyk, Ycck, Exif, Floorplan, Calliphora, Turtle, GammaDalaiLamaGray, - Hiyamugi, Jpeg400, Jpeg420Exif, Jpeg444, Ratio1x1 + Hiyamugi, Jpeg400, Jpeg420Exif, Jpeg444, + Ratio1x1, Testorig12bit }; } diff --git a/tests/Images/Input/Jpg/baseline/testorig12.jpg b/tests/Images/Input/Jpg/baseline/testorig12.jpg new file mode 100644 index 0000000000..5cc7a0ada2 --- /dev/null +++ b/tests/Images/Input/Jpg/baseline/testorig12.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:34790770f76db8e60a7765b52ca4edf5f16bc21bcb8c6045ca2efef39a8a013e +size 12394 From 84aef78c1e758f40a08e869cf05f1f5aad26f03d Mon Sep 17 00:00:00 2001 From: Stephan Vedder Date: Sun, 9 Dec 2018 12:26:17 +0100 Subject: [PATCH 002/223] Work on 12-bit support --- src/ImageSharp/Common/Tuples/Vector4Pair.cs | 16 +++---- .../Jpeg/Components/Block8x8F.Generated.cs | 18 ++++---- .../Jpeg/Components/Block8x8F.Generated.tt | 18 ++++---- .../Formats/Jpeg/Components/Block8x8F.cs | 8 ++-- .../JpegColorConverter.FromCmyk.cs | 11 +++-- .../JpegColorConverter.FromGrayScale.cs | 9 ++-- .../JpegColorConverter.FromRgb.cs | 9 ++-- .../JpegColorConverter.FromYCbCrBasic.cs | 4 +- .../JpegColorConverter.FromYCbCrSimd.cs | 26 ++++++------ .../JpegColorConverter.FromYCbCrSimdAvx2.cs | 4 +- .../JpegColorConverter.FromYccK.cs | 21 ++++++---- .../ColorConverters/JpegColorConverter.cs | 42 ++++++++++++++++--- .../Jpeg/Components/Decoder/IRawJpegData.cs | 5 +++ .../Decoder/JpegBlockPostProcessor.cs | 12 +++++- .../Decoder/JpegComponentPostProcessor.cs | 3 +- .../Decoder/JpegImagePostProcessor.cs | 2 +- .../Formats/Jpeg/JpegDecoderCore.cs | 7 +++- .../Formats/Jpg/Block8x8FTests.cs | 6 +-- .../Formats/Jpg/JpegColorConverterTests.cs | 20 ++++----- 19 files changed, 151 insertions(+), 90 deletions(-) diff --git a/src/ImageSharp/Common/Tuples/Vector4Pair.cs b/src/ImageSharp/Common/Tuples/Vector4Pair.cs index cae283d628..2a42160d65 100644 --- a/src/ImageSharp/Common/Tuples/Vector4Pair.cs +++ b/src/ImageSharp/Common/Tuples/Vector4Pair.cs @@ -37,12 +37,12 @@ namespace SixLabors.ImageSharp.Tuples this.B += other.B; } - /// - /// Downscale method, specific to Jpeg color conversion. Works only if Vector{float}.Count == 4! + /// . Works only if Ve + /// Downscale method, specific to Jpeg color conversctor{float}.Count == 4! /// TODO: Move it somewhere else. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void RoundAndDownscalePreAvx2() + internal void RoundAndDownscalePreAvx2(float downscaleFactor) { ref Vector a = ref Unsafe.As>(ref this.A); a = a.FastRound(); @@ -50,8 +50,8 @@ namespace SixLabors.ImageSharp.Tuples ref Vector b = ref Unsafe.As>(ref this.B); b = b.FastRound(); - // Downscale by 1/255 - var scale = new Vector4(1 / 255f); + // Downscale by 1/factor + var scale = new Vector4(1 / downscaleFactor); this.A *= scale; this.B *= scale; } @@ -61,14 +61,14 @@ namespace SixLabors.ImageSharp.Tuples /// TODO: Move it somewhere else. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void RoundAndDownscaleAvx2() + internal void RoundAndDownscaleAvx2(float downscaleFactor) { ref Vector self = ref Unsafe.As>(ref this); Vector v = self; v = v.FastRound(); - // Downscale by 1/255 - v *= new Vector(1 / 255f); + // Downscale by 1/factor + v *= new Vector(1 / downscaleFactor); self = v; } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs index 09ed6408d7..15fbb83d96 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs @@ -9,10 +9,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components { internal partial struct Block8x8F { - private static readonly Vector4 CMin4 = new Vector4(0F); - private static readonly Vector4 CMax4 = new Vector4(255F); - private static readonly Vector4 COff4 = new Vector4(128F); - /// /// Transpose the block into the destination block. /// @@ -94,10 +90,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components } /// - /// Level shift by +128, clip to [0, 255] + /// Level shift by +maximum/2, clip to [0, maximum] /// - public void NormalizeColorsInplace() + public void NormalizeColorsInplace(float maximum) { + Vector4 CMin4 = new Vector4(0F); + Vector4 CMax4 = new Vector4(maximum); + Vector4 COff4 = new Vector4(maximum/2 + 1); + this.V0L = Vector4.Clamp(this.V0L + COff4, CMin4, CMax4); this.V0R = Vector4.Clamp(this.V0R + COff4, CMin4, CMax4); this.V1L = Vector4.Clamp(this.V1L + COff4, CMin4, CMax4); @@ -120,10 +120,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components /// AVX2-only variant for executing and in one step. /// [MethodImpl(InliningOptions.ShortMethod)] - public void NormalizeColorsAndRoundInplaceAvx2() + public void NormalizeColorsAndRoundInplaceAvx2(float maximum) { - Vector off = new Vector(128f); - Vector max = new Vector(255F); + Vector off = new Vector(maximum/2 +1); + Vector max = new Vector(maximum); ref Vector row0 = ref Unsafe.As>(ref this.V0L); row0 = NormalizeAndRound(row0, off, max); diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt index f93ee6522d..431ba86b03 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt @@ -22,10 +22,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components { internal partial struct Block8x8F { - private static readonly Vector4 CMin4 = new Vector4(0F); - private static readonly Vector4 CMax4 = new Vector4(255F); - private static readonly Vector4 COff4 = new Vector4(128F); - /// /// Transpose the block into the destination block. /// @@ -59,10 +55,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components } /// - /// Level shift by +128, clip to [0, 255] + /// Level shift by +maximum/2, clip to [0, maximum] /// - public void NormalizeColorsInplace() + public void NormalizeColorsInplace(float maximum) { + Vector4 CMin4 = new Vector4(0F); + Vector4 CMax4 = new Vector4(maximum); + Vector4 COff4 = new Vector4(maximum/2 + 1); + <# PushIndent(" "); @@ -83,10 +83,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components /// AVX2-only variant for executing and in one step. /// [MethodImpl(InliningOptions.ShortMethod)] - public void NormalizeColorsAndRoundInplaceAvx2() + public void NormalizeColorsAndRoundInplaceAvx2(float maximum) { - Vector off = new Vector(128f); - Vector max = new Vector(255F); + Vector off = new Vector(maximum/2 +1); + Vector max = new Vector(maximum); <# for (int i = 0; i < 8; i++) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs index 81393342d6..c9c886f057 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs @@ -467,17 +467,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components } /// - /// Level shift by +128, clip to [0..255], and round all the values in the block. + /// Level shift by +maximum/2, clip to [0..maximum], and round all the values in the block. /// - public void NormalizeColorsAndRoundInplace() + public void NormalizeColorsAndRoundInplace(float maximum) { if (SimdUtils.IsAvx2CompatibleArchitecture) { - this.NormalizeColorsAndRoundInplaceAvx2(); + this.NormalizeColorsAndRoundInplaceAvx2(maximum); } else { - this.NormalizeColorsInplace(); + this.NormalizeColorsInplace(maximum); this.RoundInplace(); } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmyk.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmyk.cs index 7a14d072e6..28f0000195 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmyk.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmyk.cs @@ -10,8 +10,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters { internal class FromCmyk : JpegColorConverter { - public FromCmyk() - : base(JpegColorSpace.Cmyk) + public FromCmyk(int precision) + : base(JpegColorSpace.Cmyk, precision) { } @@ -25,14 +25,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters var v = new Vector4(0, 0, 0, 1F); - var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F); + var scale = new Vector4(1 / this.MaximumValue, + 1 / this.MaximumValue, + 1 / this.MaximumValue, + 1F); for (int i = 0; i < result.Length; i++) { float c = cVals[i]; float m = mVals[i]; float y = yVals[i]; - float k = kVals[i] / 255F; + float k = kVals[i] / this.MaximumValue; v.X = c * k; v.Y = m * k; diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs index 7424145c3b..36bd6fca99 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs @@ -12,14 +12,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters { internal class FromGrayscale : JpegColorConverter { - public FromGrayscale() - : base(JpegColorSpace.Grayscale) + public FromGrayscale(int precision) + : base(JpegColorSpace.Grayscale, precision) { } public override void ConvertToRgba(in ComponentValues values, Span result) { - var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F); + var scale = new Vector4(1 / this.MaximumValue, + 1 / this.MaximumValue, + 1 / this.MaximumValue, + 1F); ref float sBase = ref MemoryMarshal.GetReference(values.Component0); ref Vector4 dBase = ref MemoryMarshal.GetReference(result); diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgb.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgb.cs index 7cd97c4140..13eec7aad1 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgb.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgb.cs @@ -10,8 +10,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters { internal class FromRgb : JpegColorConverter { - public FromRgb() - : base(JpegColorSpace.RGB) + public FromRgb(int precision) + : base(JpegColorSpace.RGB, precision) { } @@ -24,7 +24,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters var v = new Vector4(0, 0, 0, 1); - var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F); + var scale = new Vector4(1 / this.MaximumValue, + 1 / this.MaximumValue, + 1 / this.MaximumValue, + 1F); for (int i = 0; i < result.Length; i++) { diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs index cb71889bc5..2cab95ff09 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs @@ -10,8 +10,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters { internal class FromYCbCrBasic : JpegColorConverter { - public FromYCbCrBasic() - : base(JpegColorSpace.YCbCr) + public FromYCbCrBasic(int precision) + : base(JpegColorSpace.YCbCr, precision) { } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs index 23aa1acbe6..1047957100 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs @@ -14,8 +14,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters { internal class FromYCbCrSimd : JpegColorConverter { - public FromYCbCrSimd() - : base(JpegColorSpace.YCbCr) + public FromYCbCrSimd(int precision) + : base(JpegColorSpace.YCbCr, precision) { } @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters int simdCount = result.Length - remainder; if (simdCount > 0) { - ConvertCore(values.Slice(0, simdCount), result.Slice(0, simdCount)); + ConvertCore(values.Slice(0, simdCount), result.Slice(0, simdCount), this.MaximumValue, this.HalfValue); } FromYCbCrBasic.ConvertCore(values.Slice(simdCount, remainder), result.Slice(simdCount, remainder)); @@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters /// /// SIMD convert using buffers of sizes divisible by 8. /// - internal static void ConvertCore(in ComponentValues values, Span result) + internal static void ConvertCore(in ComponentValues values, Span result, float maxValue, float halfValue) { DebugGuard.IsTrue(result.Length % 8 == 0, nameof(result), "result.Length should be divisible by 8!"); @@ -48,7 +48,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters ref Vector4Octet resultBase = ref Unsafe.As(ref MemoryMarshal.GetReference(result)); - var chromaOffset = new Vector4(-128f); + var chromaOffset = new Vector4(-halfValue); // Walking 8 elements at one step: int n = result.Length / 8; @@ -58,11 +58,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters // y = yVals[i]; Vector4Pair y = Unsafe.Add(ref yBase, i); - // cb = cbVals[i] - 128F; + // cb = cbVals[i] - halfValue); Vector4Pair cb = Unsafe.Add(ref cbBase, i); cb.AddInplace(chromaOffset); - // cr = crVals[i] - 128F; + // cr = crVals[i] - halfValue; Vector4Pair cr = Unsafe.Add(ref crBase, i); cr.AddInplace(chromaOffset); @@ -90,15 +90,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters if (Vector.Count == 4) { // TODO: Find a way to properly run & test this path on AVX2 PC-s! (Have I already mentioned that Vector is terrible?) - r.RoundAndDownscalePreAvx2(); - g.RoundAndDownscalePreAvx2(); - b.RoundAndDownscalePreAvx2(); + r.RoundAndDownscalePreAvx2(maxValue); + g.RoundAndDownscalePreAvx2(maxValue); + b.RoundAndDownscalePreAvx2(maxValue); } else if (SimdUtils.IsAvx2CompatibleArchitecture) { - r.RoundAndDownscaleAvx2(); - g.RoundAndDownscaleAvx2(); - b.RoundAndDownscaleAvx2(); + r.RoundAndDownscaleAvx2(maxValue); + g.RoundAndDownscaleAvx2(maxValue); + b.RoundAndDownscaleAvx2(maxValue); } else { diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs index f0a70a6f38..4696959950 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs @@ -15,8 +15,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters { internal class FromYCbCrSimdAvx2 : JpegColorConverter { - public FromYCbCrSimdAvx2() - : base(JpegColorSpace.YCbCr) + public FromYCbCrSimdAvx2(int precision) + : base(JpegColorSpace.YCbCr, precision) { } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccK.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccK.cs index 6f940f62f9..d4e1510156 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccK.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccK.cs @@ -10,8 +10,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters { internal class FromYccK : JpegColorConverter { - public FromYccK() - : base(JpegColorSpace.Ycck) + public FromYccK(int precision) + : base(JpegColorSpace.Ycck, precision) { } @@ -25,18 +25,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters var v = new Vector4(0, 0, 0, 1F); - var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F); + var scale = new Vector4(1 / this.MaximumValue, + 1 / this.MaximumValue, + 1 / this.MaximumValue, + 1F); for (int i = 0; i < result.Length; i++) { float y = yVals[i]; - float cb = cbVals[i] - 128F; - float cr = crVals[i] - 128F; - float k = kVals[i] / 255F; + float cb = cbVals[i] - this.HalfValue; + float cr = crVals[i] - this.HalfValue; + float k = kVals[i] / this.MaximumValue; - v.X = (255F - MathF.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero)) * k; - v.Y = (255F - MathF.Round(y - (0.344136F * cb) - (0.714136F * cr), MidpointRounding.AwayFromZero)) * k; - v.Z = (255F - MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero)) * k; + v.X = (this.MaximumValue - MathF.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero)) * k; + v.Y = (this.MaximumValue - MathF.Round(y - (0.344136F * cb) - (0.714136F * cr), MidpointRounding.AwayFromZero)) * k; + v.Z = (this.MaximumValue - MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero)) * k; v.W = 1F; v *= scale; diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs index a44ebf89d1..30dbdc8964 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs @@ -22,15 +22,29 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters /// private static readonly JpegColorConverter[] Converters = { - GetYCbCrConverter(), new FromYccK(), new FromCmyk(), new FromGrayscale(), new FromRgb() + // 8-bit converters + GetYCbCrConverter(8), + new FromYccK(8), + new FromCmyk(8), + new FromGrayscale(8), + new FromRgb(8), + // 12-bit converters + GetYCbCrConverter(12), + new FromYccK(12), + new FromCmyk(12), + new FromGrayscale(12), + new FromRgb(12), }; /// /// Initializes a new instance of the class. /// - protected JpegColorConverter(JpegColorSpace colorSpace) + protected JpegColorConverter(JpegColorSpace colorSpace, int precision) { this.ColorSpace = colorSpace; + this.Precision = precision; + this.MaximumValue = (float)Math.Pow(2, precision) - 1; + this.HalfValue = (float)Math.Ceiling(this.MaximumValue / 2); } /// @@ -38,12 +52,28 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters /// public JpegColorSpace ColorSpace { get; } + /// + /// Gets the Precision of this converter in bits. + /// + public int Precision { get; } + + /// + /// Gets the maximum value of a sample + /// + private float MaximumValue { get; } + + /// + /// Gets the maximum value of a sample + /// + private float HalfValue { get; } + /// /// Returns the corresponding to the given /// - public static JpegColorConverter GetConverter(JpegColorSpace colorSpace) + public static JpegColorConverter GetConverter(JpegColorSpace colorSpace, float precision) { - JpegColorConverter converter = Converters.FirstOrDefault(c => c.ColorSpace == colorSpace); + JpegColorConverter converter = Converters.FirstOrDefault(c => c.ColorSpace == colorSpace && + c.Precision == precision); if (converter is null) { @@ -63,8 +93,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters /// /// Returns the for the YCbCr colorspace that matches the current CPU architecture. /// - private static JpegColorConverter GetYCbCrConverter() => - FromYCbCrSimdAvx2.IsAvailable ? (JpegColorConverter)new FromYCbCrSimdAvx2() : new FromYCbCrSimd(); + private static JpegColorConverter GetYCbCrConverter(int precision) => + FromYCbCrSimdAvx2.IsAvailable ? (JpegColorConverter)new FromYCbCrSimdAvx2(precision) : new FromYCbCrSimd(precision); /// /// A stack-only struct to reference the input buffers using -s. diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs index 1454bb5b12..83d65c0421 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs @@ -29,6 +29,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// JpegColorSpace ColorSpace { get; } + /// + /// Gets the number of bits used for precision. + /// + int Precision { get; } + /// /// Gets the components. /// diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs index da4b2847b8..b034198cfa 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; using SixLabors.Primitives; @@ -38,6 +39,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// private Size subSamplingDivisors; + /// + /// Defines the maximum value derived from the bitdepth + /// + private int maximumValue; + /// /// Initializes a new instance of the struct. /// @@ -48,6 +54,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder int qtIndex = component.QuantizationTableIndex; this.DequantiazationTable = ZigZag.CreateDequantizationTable(ref decoder.QuantizationTables[qtIndex]); this.subSamplingDivisors = component.SubSamplingDivisors; + this.maximumValue = (int)Math.Pow(2, decoder.Precision) - 1; this.SourceBlock = default; this.WorkspaceBlock1 = default; @@ -65,7 +72,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// The destination buffer area. public void ProcessBlockColorsInto( ref Block8x8 sourceBlock, - in BufferArea destArea) + in BufferArea destArea, + float maximumValue) { ref Block8x8F b = ref this.SourceBlock; b.LoadFrom(ref sourceBlock); @@ -78,7 +86,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder // To conform better to libjpeg we actually NEED TO loose precision here. // This is because they store blocks as Int16 between all the operations. // To be "more accurate", we need to emulate this by rounding! - this.WorkspaceBlock1.NormalizeColorsAndRoundInplace(); + this.WorkspaceBlock1.NormalizeColorsAndRoundInplace(maximumValue); this.WorkspaceBlock1.CopyTo(destArea, this.subSamplingDivisors.Width, this.subSamplingDivisors.Height); } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs index 94ec600dd5..66c9245a30 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs @@ -78,6 +78,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder public void CopyBlocksToColorBuffer() { var blockPp = new JpegBlockPostProcessor(this.ImagePostProcessor.RawJpeg, this.Component); + float maximumValue = (float)Math.Pow(2,this.ImagePostProcessor.RawJpeg.Precision) - 1; for (int y = 0; y < this.BlockRowsPerStep; y++) { @@ -105,7 +106,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder this.blockAreaSize.Width, this.blockAreaSize.Height); - blockPp.ProcessBlockColorsInto(ref block, destArea); + blockPp.ProcessBlockColorsInto(ref block, destArea, maximumValue); } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs index 7ce86b4c9b..6b59955153 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs @@ -64,7 +64,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder MemoryAllocator memoryAllocator = configuration.MemoryAllocator; this.ComponentProcessors = rawJpeg.Components.Select(c => new JpegComponentPostProcessor(memoryAllocator, this, c)).ToArray(); this.rgbaBuffer = memoryAllocator.Allocate(rawJpeg.ImageSizeInPixels.Width); - this.colorConverter = JpegColorConverter.GetConverter(rawJpeg.ColorSpace); + this.colorConverter = JpegColorConverter.GetConverter(rawJpeg.ColorSpace, rawJpeg.Precision); } /// diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 5bfe88cc63..fb10425bd3 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -160,6 +160,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// public JpegColorSpace ColorSpace { get; private set; } + /// + public int Precision { get; private set; } + /// /// Gets the components. /// @@ -721,11 +724,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg this.InputStream.Read(this.temp, 0, remaining); // We only support 8-bit and 12-bit precision. - if (!SupportedPrecisions.Contains(this.temp[0])) + if (!this.SupportedPrecisions.Contains(this.temp[0])) { throw new ImageFormatException("Only 8-Bit and 12-Bit precision supported."); } + this.Precision = this.temp[0]; + this.Frame = new JpegFrame { Extended = frameMarker.Marker == JpegConstants.Markers.SOF1, diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs index 81c76390c1..7e7218c9dc 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs @@ -228,7 +228,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg this.PrintLinearData(input); Block8x8F dest = block; - dest.NormalizeColorsInplace(); + dest.NormalizeColorsInplace(255); float[] array = new float[64]; dest.CopyTo(array); @@ -253,11 +253,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg Block8x8F source = CreateRandomFloatBlock(-200, 200, seed); Block8x8F expected = source; - expected.NormalizeColorsInplace(); + expected.NormalizeColorsInplace(255); expected.RoundInplace(); Block8x8F actual = source; - actual.NormalizeColorsAndRoundInplaceAvx2(); + actual.NormalizeColorsAndRoundInplaceAvx2(255); this.Output.WriteLine(expected.ToString()); this.Output.WriteLine(actual.ToString()); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs index 8e30eb9e5d..caaad73c9f 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs @@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg public void ConvertFromYCbCrBasic(int inputBufferLength, int resultBufferLength, int seed) { ValidateRgbToYCbCrConversion( - new JpegColorConverter.FromYCbCrBasic(), + new JpegColorConverter.FromYCbCrBasic(8), 3, inputBufferLength, resultBufferLength, @@ -75,7 +75,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg JpegColorConverter.ComponentValues values = CreateRandomValues(3, size, seed); var result = new Vector4[size]; - JpegColorConverter.FromYCbCrSimd.ConvertCore(values, result); + JpegColorConverter.FromYCbCrSimd.ConvertCore(values, result, 255, 128); for (int i = 0; i < size; i++) { @@ -88,7 +88,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg public void FromYCbCrSimd(int inputBufferLength, int resultBufferLength, int seed) { ValidateRgbToYCbCrConversion( - new JpegColorConverter.FromYCbCrSimd(), + new JpegColorConverter.FromYCbCrSimd(8), 3, inputBufferLength, resultBufferLength, @@ -108,7 +108,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg //JpegColorConverter.FromYCbCrSimdAvx2.LogPlz = s => this.Output.WriteLine(s); ValidateRgbToYCbCrConversion( - new JpegColorConverter.FromYCbCrSimdAvx2(), + new JpegColorConverter.FromYCbCrSimdAvx2(8), 3, inputBufferLength, resultBufferLength, @@ -140,7 +140,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg JpegColorConverter.ComponentValues values = CreateRandomValues(3, count, 1); var result = new Vector4[count]; - JpegColorConverter converter = simd ? (JpegColorConverter)new JpegColorConverter.FromYCbCrSimd() : new JpegColorConverter.FromYCbCrBasic(); + JpegColorConverter converter = simd ? (JpegColorConverter)new JpegColorConverter.FromYCbCrSimd(8) : new JpegColorConverter.FromYCbCrBasic(8); // Warm up: converter.ConvertToRgba(values, result); @@ -161,7 +161,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg var v = new Vector4(0, 0, 0, 1F); var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F); - var converter = JpegColorConverter.GetConverter(JpegColorSpace.Cmyk); + var converter = JpegColorConverter.GetConverter(JpegColorSpace.Cmyk, 8); JpegColorConverter.ComponentValues values = CreateRandomValues(4, inputBufferLength, seed); var result = new Vector4[resultBufferLength]; @@ -194,7 +194,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [MemberData(nameof(CommonConversionData))] public void ConvertFromGrayScale(int inputBufferLength, int resultBufferLength, int seed) { - var converter = JpegColorConverter.GetConverter(JpegColorSpace.Grayscale); + var converter = JpegColorConverter.GetConverter(JpegColorSpace.Grayscale, 8); JpegColorConverter.ComponentValues values = CreateRandomValues(1, inputBufferLength, seed); var result = new Vector4[resultBufferLength]; @@ -216,7 +216,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [MemberData(nameof(CommonConversionData))] public void ConvertFromRgb(int inputBufferLength, int resultBufferLength, int seed) { - var converter = JpegColorConverter.GetConverter(JpegColorSpace.RGB); + var converter = JpegColorConverter.GetConverter(JpegColorSpace.RGB, 8); JpegColorConverter.ComponentValues values = CreateRandomValues(3, inputBufferLength, seed); var result = new Vector4[resultBufferLength]; @@ -243,7 +243,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg var v = new Vector4(0, 0, 0, 1F); var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F); - var converter = JpegColorConverter.GetConverter(JpegColorSpace.Ycck); + var converter = JpegColorConverter.GetConverter(JpegColorSpace.Ycck, 8); JpegColorConverter.ComponentValues values = CreateRandomValues(4, inputBufferLength, seed); var result = new Vector4[resultBufferLength]; @@ -308,7 +308,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg int seed) { ValidateRgbToYCbCrConversion( - JpegColorConverter.GetConverter(colorSpace), + JpegColorConverter.GetConverter(colorSpace,8), componentCount, inputBufferLength, resultBufferLength, From aa8591950e701b8f8409b1f8b3c3766c37e904c7 Mon Sep 17 00:00:00 2001 From: Stephan Vedder Date: Sun, 9 Dec 2018 18:26:23 +0100 Subject: [PATCH 003/223] Add missing Non-SIMD implementation --- .../Formats/Jpeg/Components/Block8x8F.Generated.cs | 5 +++-- .../Formats/Jpeg/Components/Block8x8F.Generated.tt | 5 +++-- .../JpegColorConverter.FromYCbCrBasic.cs | 10 +++++----- .../JpegColorConverter.FromYCbCrSimd.cs | 3 ++- .../JpegColorConverter.FromYCbCrSimdAvx2.cs | 12 +++++++----- .../Codecs/Jpeg/YCbCrColorConversion.cs | 6 +++--- 6 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs index 15fbb83d96..1f47de594c 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; using System.Numerics; using System.Runtime.CompilerServices; @@ -96,7 +97,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components { Vector4 CMin4 = new Vector4(0F); Vector4 CMax4 = new Vector4(maximum); - Vector4 COff4 = new Vector4(maximum/2 + 1); + Vector4 COff4 = new Vector4((float)Math.Ceiling(maximum/2)); this.V0L = Vector4.Clamp(this.V0L + COff4, CMin4, CMax4); this.V0R = Vector4.Clamp(this.V0R + COff4, CMin4, CMax4); @@ -122,7 +123,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components [MethodImpl(InliningOptions.ShortMethod)] public void NormalizeColorsAndRoundInplaceAvx2(float maximum) { - Vector off = new Vector(maximum/2 +1); + Vector off = new Vector((float)Math.Ceiling(maximum/2)); Vector max = new Vector(maximum); ref Vector row0 = ref Unsafe.As>(ref this.V0L); diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt index 431ba86b03..ec4e06e429 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt @@ -11,6 +11,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; using System.Numerics; using System.Runtime.CompilerServices; @@ -61,7 +62,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components { Vector4 CMin4 = new Vector4(0F); Vector4 CMax4 = new Vector4(maximum); - Vector4 COff4 = new Vector4(maximum/2 + 1); + Vector4 COff4 = new Vector4((float)Math.Ceiling(maximum/2)); <# @@ -85,7 +86,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components [MethodImpl(InliningOptions.ShortMethod)] public void NormalizeColorsAndRoundInplaceAvx2(float maximum) { - Vector off = new Vector(maximum/2 +1); + Vector off = new Vector((float)Math.Ceiling(maximum/2)); Vector max = new Vector(maximum); <# diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs index 2cab95ff09..124aac1224 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs @@ -17,10 +17,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters public override void ConvertToRgba(in ComponentValues values, Span result) { - ConvertCore(values, result); + ConvertCore(values, result, this.MaximumValue, this.HalfValue); } - internal static void ConvertCore(in ComponentValues values, Span result) + internal static void ConvertCore(in ComponentValues values, Span result, float maxValue, float halfValue) { // TODO: We can optimize a lot here with Vector and SRCS.Unsafe()! ReadOnlySpan yVals = values.Component0; @@ -29,13 +29,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters var v = new Vector4(0, 0, 0, 1); - var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F); + var scale = new Vector4(1 / maxValue, 1 / maxValue, 1 / maxValue, 1F); for (int i = 0; i < result.Length; i++) { float y = yVals[i]; - float cb = cbVals[i] - 128F; - float cr = crVals[i] - 128F; + float cb = cbVals[i] - halfValue; + float cr = crVals[i] - halfValue; v.X = MathF.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero); v.Y = MathF.Round(y - (0.344136F * cb) - (0.714136F * cr), MidpointRounding.AwayFromZero); diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs index 1047957100..5de6f2ffb0 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs @@ -28,7 +28,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters ConvertCore(values.Slice(0, simdCount), result.Slice(0, simdCount), this.MaximumValue, this.HalfValue); } - FromYCbCrBasic.ConvertCore(values.Slice(simdCount, remainder), result.Slice(simdCount, remainder)); + FromYCbCrBasic.ConvertCore(values.Slice(simdCount, remainder), result.Slice(simdCount, remainder), + this.MaximumValue, this.HalfValue); } /// diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs index 4696959950..c34bc17ad5 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs @@ -28,16 +28,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters int simdCount = result.Length - remainder; if (simdCount > 0) { - ConvertCore(values.Slice(0, simdCount), result.Slice(0, simdCount)); + ConvertCore(values.Slice(0, simdCount), result.Slice(0, simdCount), + this.MaximumValue, this.HalfValue); } - FromYCbCrBasic.ConvertCore(values.Slice(simdCount, remainder), result.Slice(simdCount, remainder)); + FromYCbCrBasic.ConvertCore(values.Slice(simdCount, remainder), result.Slice(simdCount, remainder), + this.MaximumValue, this.HalfValue);; } /// /// SIMD convert using buffers of sizes divisible by 8. /// - internal static void ConvertCore(in ComponentValues values, Span result) + internal static void ConvertCore(in ComponentValues values, Span result, float maxValue, float halfValue) { // This implementation is actually AVX specific. // An AVX register is capable of storing 8 float-s. @@ -57,7 +59,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters ref Vector4Octet resultBase = ref Unsafe.As(ref MemoryMarshal.GetReference(result)); - var chromaOffset = new Vector(-128f); + var chromaOffset = new Vector(-halfValue); // Walking 8 elements at one step: int n = result.Length / 8; @@ -70,7 +72,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters ref Vector ggRefAsVector = ref Unsafe.As>(ref gg); ref Vector bbRefAsVector = ref Unsafe.As>(ref bb); - var scale = new Vector(1 / 255f); + var scale = new Vector(1 / maxValue); for (int i = 0; i < n; i++) { diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/YCbCrColorConversion.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/YCbCrColorConversion.cs index 05edd27919..8417b32f27 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/YCbCrColorConversion.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/YCbCrColorConversion.cs @@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg { var values = new JpegColorConverter.ComponentValues(this.input, 0); - JpegColorConverter.FromYCbCrBasic.ConvertCore(values, this.output); + JpegColorConverter.FromYCbCrBasic.ConvertCore(values, this.output, 255F, 128F); } [Benchmark] @@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg { var values = new JpegColorConverter.ComponentValues(this.input, 0); - JpegColorConverter.FromYCbCrSimd.ConvertCore(values, this.output); + JpegColorConverter.FromYCbCrSimd.ConvertCore(values, this.output, 255F, 128F); } [Benchmark] @@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg { var values = new JpegColorConverter.ComponentValues(this.input, 0); - JpegColorConverter.FromYCbCrSimdAvx2.ConvertCore(values, this.output); + JpegColorConverter.FromYCbCrSimdAvx2.ConvertCore(values, this.output, 255F, 128F); } private static Buffer2D[] CreateRandomValues( From 09c48226e45355e2b27ed9c6cadc5f9b25ca6333 Mon Sep 17 00:00:00 2001 From: Stephan Vedder Date: Mon, 10 Dec 2018 18:39:34 +0100 Subject: [PATCH 004/223] Fix style issues --- src/ImageSharp/Common/Tuples/Vector4Pair.cs | 5 ++--- .../ColorConverters/JpegColorConverter.FromCmyk.cs | 9 +++++---- .../ColorConverters/JpegColorConverter.FromGrayScale.cs | 9 +++++---- .../ColorConverters/JpegColorConverter.FromRgb.cs | 9 +++++---- .../ColorConverters/JpegColorConverter.FromYCbCrSimd.cs | 3 +-- .../JpegColorConverter.FromYCbCrSimdAvx2.cs | 6 ++---- .../ColorConverters/JpegColorConverter.FromYccK.cs | 9 +++++---- .../Decoder/ColorConverters/JpegColorConverter.cs | 3 ++- .../Jpeg/Components/Decoder/JpegBlockPostProcessor.cs | 4 +++- src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs | 4 ++-- 10 files changed, 32 insertions(+), 29 deletions(-) diff --git a/src/ImageSharp/Common/Tuples/Vector4Pair.cs b/src/ImageSharp/Common/Tuples/Vector4Pair.cs index 2a42160d65..2e5ca31371 100644 --- a/src/ImageSharp/Common/Tuples/Vector4Pair.cs +++ b/src/ImageSharp/Common/Tuples/Vector4Pair.cs @@ -37,9 +37,8 @@ namespace SixLabors.ImageSharp.Tuples this.B += other.B; } - /// . Works only if Ve - /// Downscale method, specific to Jpeg color conversctor{float}.Count == 4! - /// TODO: Move it somewhere else. + /// . + /// Downscale method, specific to Jpeg color conversion. Works only if Vector{float}.Count == 4! /// TODO: Move it somewhere else. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void RoundAndDownscalePreAvx2(float downscaleFactor) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmyk.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmyk.cs index 28f0000195..d4dc31fe0c 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmyk.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmyk.cs @@ -25,10 +25,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters var v = new Vector4(0, 0, 0, 1F); - var scale = new Vector4(1 / this.MaximumValue, - 1 / this.MaximumValue, - 1 / this.MaximumValue, - 1F); + var scale = new Vector4( + 1 / this.MaximumValue, + 1 / this.MaximumValue, + 1 / this.MaximumValue, + 1F); for (int i = 0; i < result.Length; i++) { diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs index 36bd6fca99..4a5dfa6322 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs @@ -19,10 +19,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters public override void ConvertToRgba(in ComponentValues values, Span result) { - var scale = new Vector4(1 / this.MaximumValue, - 1 / this.MaximumValue, - 1 / this.MaximumValue, - 1F); + var scale = new Vector4( + 1 / this.MaximumValue, + 1 / this.MaximumValue, + 1 / this.MaximumValue, + 1F); ref float sBase = ref MemoryMarshal.GetReference(values.Component0); ref Vector4 dBase = ref MemoryMarshal.GetReference(result); diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgb.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgb.cs index 13eec7aad1..516dfb39fe 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgb.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgb.cs @@ -24,10 +24,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters var v = new Vector4(0, 0, 0, 1); - var scale = new Vector4(1 / this.MaximumValue, - 1 / this.MaximumValue, - 1 / this.MaximumValue, - 1F); + var scale = new Vector4( + 1 / this.MaximumValue, + 1 / this.MaximumValue, + 1 / this.MaximumValue, + 1F); for (int i = 0; i < result.Length; i++) { diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs index 5de6f2ffb0..10ef02a931 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs @@ -28,8 +28,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters ConvertCore(values.Slice(0, simdCount), result.Slice(0, simdCount), this.MaximumValue, this.HalfValue); } - FromYCbCrBasic.ConvertCore(values.Slice(simdCount, remainder), result.Slice(simdCount, remainder), - this.MaximumValue, this.HalfValue); + FromYCbCrBasic.ConvertCore(values.Slice(simdCount, remainder), result.Slice(simdCount, remainder), this.MaximumValue, this.HalfValue); } /// diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs index c34bc17ad5..9953f78c18 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs @@ -28,12 +28,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters int simdCount = result.Length - remainder; if (simdCount > 0) { - ConvertCore(values.Slice(0, simdCount), result.Slice(0, simdCount), - this.MaximumValue, this.HalfValue); + ConvertCore(values.Slice(0, simdCount), result.Slice(0, simdCount), this.MaximumValue, this.HalfValue); } - FromYCbCrBasic.ConvertCore(values.Slice(simdCount, remainder), result.Slice(simdCount, remainder), - this.MaximumValue, this.HalfValue);; + FromYCbCrBasic.ConvertCore(values.Slice(simdCount, remainder), result.Slice(simdCount, remainder), this.MaximumValue, this.HalfValue); } /// diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccK.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccK.cs index d4e1510156..94be11e237 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccK.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccK.cs @@ -25,10 +25,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters var v = new Vector4(0, 0, 0, 1F); - var scale = new Vector4(1 / this.MaximumValue, - 1 / this.MaximumValue, - 1 / this.MaximumValue, - 1F); + var scale = new Vector4( + 1 / this.MaximumValue, + 1 / this.MaximumValue, + 1 / this.MaximumValue, + 1F); for (int i = 0; i < result.Length; i++) { diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs index 30dbdc8964..adcb49bef5 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs @@ -28,6 +28,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters new FromCmyk(8), new FromGrayscale(8), new FromRgb(8), + // 12-bit converters GetYCbCrConverter(12), new FromYccK(12), @@ -63,7 +64,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters private float MaximumValue { get; } /// - /// Gets the maximum value of a sample + /// Gets the half of the maximum value of a sample /// private float HalfValue { get; } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs index b034198cfa..ad94572d9a 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs @@ -65,11 +65,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// Processes 'sourceBlock' producing Jpeg color channel values from spectral values: /// - Dequantize /// - Applying IDCT - /// - Level shift by +128, clip to [0, 255] + /// - Level shift by +maximumValue/2, clip to [0, maximumValue] /// - Copy the resulting color values into 'destArea' scaling up the block by amount defined in /// /// The source block. /// The destination buffer area. + /// The maximum value derived from the bitdepth. + public void ProcessBlockColorsInto( ref Block8x8 sourceBlock, in BufferArea destArea, diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index fb10425bd3..c52858d4c3 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// /// The only supported precision /// - public readonly int[] SupportedPrecisions = { 8, 12 }; + private readonly int[] supportedPrecisions = { 8, 12 }; /// /// The global configuration @@ -724,7 +724,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg this.InputStream.Read(this.temp, 0, remaining); // We only support 8-bit and 12-bit precision. - if (!this.SupportedPrecisions.Contains(this.temp[0])) + if (!this.supportedPrecisions.Contains(this.temp[0])) { throw new ImageFormatException("Only 8-Bit and 12-Bit precision supported."); } From 324f7129e0c7a8c8ba34a20ba827cfc06fe61915 Mon Sep 17 00:00:00 2001 From: Stephan Vedder Date: Mon, 10 Dec 2018 23:40:26 +0100 Subject: [PATCH 005/223] Fix style issues --- .../Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs | 1 - .../Jpeg/Components/Decoder/JpegComponentPostProcessor.cs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs index ad94572d9a..fe39f41884 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs @@ -71,7 +71,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// The source block. /// The destination buffer area. /// The maximum value derived from the bitdepth. - public void ProcessBlockColorsInto( ref Block8x8 sourceBlock, in BufferArea destArea, diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs index 66c9245a30..e7f3e4fda5 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs @@ -78,7 +78,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder public void CopyBlocksToColorBuffer() { var blockPp = new JpegBlockPostProcessor(this.ImagePostProcessor.RawJpeg, this.Component); - float maximumValue = (float)Math.Pow(2,this.ImagePostProcessor.RawJpeg.Precision) - 1; + float maximumValue = (float)Math.Pow(2, this.ImagePostProcessor.RawJpeg.Precision) - 1; for (int y = 0; y < this.BlockRowsPerStep; y++) { From 401144a421d05ccfddb4e2e7fb458f072a56cf53 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 21 Dec 2018 17:57:24 +1100 Subject: [PATCH 006/223] Minor optimizations --- .../ColorConverters/JpegColorConverter.cs | 6 ++---- .../Jpeg/Components/Decoder/IRawJpegData.cs | 2 +- .../Decoder/JpegImagePostProcessor.cs | 17 +++++++++++++---- src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs | 3 +-- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs index adcb49bef5..c2e390c590 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs @@ -3,12 +3,10 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Numerics; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Tuples; -using SixLabors.Memory; namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters { @@ -73,8 +71,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters /// public static JpegColorConverter GetConverter(JpegColorSpace colorSpace, float precision) { - JpegColorConverter converter = Converters.FirstOrDefault(c => c.ColorSpace == colorSpace && - c.Precision == precision); + JpegColorConverter converter = Array.Find(Converters, c => c.ColorSpace == colorSpace + && c.Precision == precision); if (converter is null) { diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs index 83d65c0421..ace8d7215b 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs @@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// /// Gets the components. /// - IEnumerable Components { get; } + IJpegComponent[] Components { get; } /// /// Gets the quantization tables, in zigzag order. diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs index 6b59955153..438749abf9 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs @@ -3,7 +3,6 @@ using System; using System.Buffers; -using System.Linq; using System.Numerics; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; @@ -57,12 +56,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { this.configuration = configuration; this.RawJpeg = rawJpeg; - IJpegComponent c0 = rawJpeg.Components.First(); + IJpegComponent c0 = rawJpeg.Components[0]; this.NumberOfPostProcessorSteps = c0.SizeInBlocks.Height / BlockRowsPerStep; this.PostProcessorBufferSize = new Size(c0.SizeInBlocks.Width * 8, PixelRowsPerStep); MemoryAllocator memoryAllocator = configuration.MemoryAllocator; - this.ComponentProcessors = rawJpeg.Components.Select(c => new JpegComponentPostProcessor(memoryAllocator, this, c)).ToArray(); + + this.ComponentProcessors = new JpegComponentPostProcessor[rawJpeg.Components.Length]; + for (int i = 0; i < rawJpeg.Components.Length; i++) + { + this.ComponentProcessors[i] = new JpegComponentPostProcessor(memoryAllocator, this, rawJpeg.Components[i]); + } + this.rgbaBuffer = memoryAllocator.Allocate(rawJpeg.ImageSizeInPixels.Width); this.colorConverter = JpegColorConverter.GetConverter(rawJpeg.ColorSpace, rawJpeg.Precision); } @@ -152,7 +157,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { int maxY = Math.Min(destination.Height, this.PixelRowCounter + PixelRowsPerStep); - Buffer2D[] buffers = this.ComponentProcessors.Select(cp => cp.ColorBuffer).ToArray(); + var buffers = new Buffer2D[this.ComponentProcessors.Length]; + for (int i = 0; i < this.ComponentProcessors.Length; i++) + { + buffers[i] = this.ComponentProcessors[i].ColorBuffer; + } for (int yy = this.PixelRowCounter; yy < maxY; yy++) { diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index af6dfd9ec4..02ae022e11 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -3,7 +3,6 @@ using System; using System.Buffers.Binary; -using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.CompilerServices; @@ -169,7 +168,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg public JpegComponent[] Components => this.Frame.Components; /// - IEnumerable IRawJpegData.Components => this.Components; + IJpegComponent[] IRawJpegData.Components => this.Components; /// public Block8x8F[] QuantizationTables { get; private set; } From b67b5b36864faefa890bac201e993b1ff87fd802 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 21 Dec 2018 18:13:19 +1100 Subject: [PATCH 007/223] Update reference images and decoder --- tests/ImageSharp.Tests/ImageSharp.Tests.csproj | 2 +- tests/Images/External | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index 7452d6e498..e20f1514cc 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -27,7 +27,7 @@ - + diff --git a/tests/Images/External b/tests/Images/External index 69603ee5b6..7ada45bc34 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 69603ee5b6f7dd64114fc44d321e50d9b2d439be +Subproject commit 7ada45bc3484f40e28a50817386ca93f293acd11 From 880be0a5aa3feadad173775bea6fa8e50ef3e212 Mon Sep 17 00:00:00 2001 From: Peter Amrehn Date: Fri, 4 Jan 2019 21:37:24 +0100 Subject: [PATCH 008/223] #797 throw ImageFormatException when no StartOfFrame marker is found on a jpg image. --- src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs | 5 +++++ .../Formats/Jpg/JpegDecoderTests.Baseline.cs | 8 ++++++++ tests/ImageSharp.Tests/TestImages.cs | 1 + tests/Images/Input/Jpg/issues/Issue797-InvalidImage.jpg | 3 +++ 4 files changed, 17 insertions(+) create mode 100644 tests/Images/Input/Jpg/issues/Issue797-InvalidImage.jpg diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 02ae022e11..936fc0631b 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -292,6 +292,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg case JpegConstants.Markers.SOS: if (!metadataOnly) { + if (this.Frame is null) + { + throw new ImageFormatException("no readable SOF marker found."); + } + this.ProcessStartOfScanMarker(); break; } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs index 73167a4b7e..a2f57e485a 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs @@ -42,5 +42,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg // TODO: We need a public ImageDecoderException class in ImageSharp! Assert.ThrowsAny(() => provider.GetImage(JpegDecoder)); } + + [Theory] + [WithFile(TestImages.Jpeg.Issues.InvalidJpegThrowsWrongException797, PixelTypes.Rgba32)] + public void LoadingImage_InvalidTagLength_ShouldThrow(TestImageProvider provider) + where TPixel : struct, IPixel + { + Assert.Throws(() => provider.GetImage()); + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 6141e9e2a0..460aceba0a 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -167,6 +167,7 @@ namespace SixLabors.ImageSharp.Tests public const string OrderedInterleavedProgressive723C = "Jpg/issues/Issue723-Ordered-Interleaved-Progressive-C.jpg"; public const string ExifGetString750Transform = "Jpg/issues/issue750-exif-tranform.jpg"; public const string ExifGetString750Load = "Jpg/issues/issue750-exif-load.jpg"; + public const string InvalidJpegThrowsWrongException797 = "Jpg/issues/Issue797-InvalidImage.jpg"; } public static readonly string[] All = Baseline.All.Concat(Progressive.All).ToArray(); diff --git a/tests/Images/Input/Jpg/issues/Issue797-InvalidImage.jpg b/tests/Images/Input/Jpg/issues/Issue797-InvalidImage.jpg new file mode 100644 index 0000000000..7c9190fbdb --- /dev/null +++ b/tests/Images/Input/Jpg/issues/Issue797-InvalidImage.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2c3c6a09c1ed868bb18c7c5038da743a88dbfd10762a2b12260053adf63b376f +size 381 From 1359bceb45674cf3fccdd6774c639cf62d7dd61c Mon Sep 17 00:00:00 2001 From: Peter Amrehn Date: Sat, 5 Jan 2019 00:45:43 +0100 Subject: [PATCH 009/223] #797 - apply review remarks --- src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 936fc0631b..06c844d58e 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -292,11 +292,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg case JpegConstants.Markers.SOS: if (!metadataOnly) { - if (this.Frame is null) - { - throw new ImageFormatException("no readable SOF marker found."); - } - this.ProcessStartOfScanMarker(); break; } @@ -864,6 +859,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// private void ProcessStartOfScanMarker() { + if (this.Frame is null) + { + throw new ImageFormatException("No readable SOFn (Start Of Frame) marker found."); + } + int selectorsCount = this.InputStream.ReadByte(); for (int i = 0; i < selectorsCount; i++) { From 78f0a41f0366b61f3986aace87ca560c20c63afa Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 9 Jan 2019 01:16:37 +1100 Subject: [PATCH 010/223] Use bounds checks in Huffman ctr. Fix #798 --- .../Jpeg/Components/Decoder/HuffmanTable.cs | 23 +++++++++++-------- .../Formats/Jpg/JpegDecoderTests.Baseline.cs | 10 ++++---- tests/ImageSharp.Tests/TestImages.cs | 1 + .../Issue798-AccessViolationException.jpg | 3 +++ 4 files changed, 23 insertions(+), 14 deletions(-) create mode 100644 tests/Images/Input/Jpg/issues/Issue798-AccessViolationException.jpg diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs index 9e134746bc..9e11981b12 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs @@ -50,8 +50,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// The huffman values public HuffmanTable(MemoryAllocator memoryAllocator, ReadOnlySpan codeLengths, ReadOnlySpan values) { - const int Length = 257; - using (IMemoryOwner huffcode = memoryAllocator.Allocate(Length)) + // We do some bounds checks in the code here to protect against AccessViolationExceptions + const int HuffCodeLength = 257; + const int MaxSizeLength = HuffCodeLength - 1; + using (IMemoryOwner huffcode = memoryAllocator.Allocate(HuffCodeLength)) { ref short huffcodeRef = ref MemoryMarshal.GetReference(huffcode.GetSpan()); ref byte codeLengthsRef = ref MemoryMarshal.GetReference(codeLengths); @@ -63,7 +65,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder for (short i = 1; i < 17; i++) { byte length = Unsafe.Add(ref codeLengthsRef, i); - for (short j = 0; j < length; j++) + for (short j = 0; j < length && x < MaxSizeLength; j++) { Unsafe.Add(ref sizesRef, x++) = i; } @@ -84,7 +86,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder Unsafe.Add(ref valOffsetRef, k) = (int)(si - code); if (Unsafe.Add(ref sizesRef, si) == k) { - while (Unsafe.Add(ref sizesRef, si) == k) + while (Unsafe.Add(ref sizesRef, si) == k && si < HuffCodeLength) { Unsafe.Add(ref huffcodeRef, si++) = (short)code++; } @@ -100,19 +102,20 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder // Generate non-spec lookup tables to speed up decoding. const int FastBits = ScanDecoder.FastBits; - ref byte fastRef = ref this.Lookahead[0]; - Unsafe.InitBlockUnaligned(ref fastRef, 0xFF, 1 << FastBits); // Flag for non-accelerated + ref byte lookaheadRef = ref this.Lookahead[0]; + const uint MaxFastLength = 1 << FastBits; + Unsafe.InitBlockUnaligned(ref lookaheadRef, 0xFF, MaxFastLength); // Flag for non-accelerated for (int i = 0; i < si; i++) { int size = Unsafe.Add(ref sizesRef, i); if (size <= FastBits) { - int c = Unsafe.Add(ref huffcodeRef, i) << (FastBits - size); - int m = 1 << (FastBits - size); - for (int l = 0; l < m; l++) + int huffCode = Unsafe.Add(ref huffcodeRef, i) << (FastBits - size); + int max = 1 << (FastBits - size); + for (int left = 0; left < max; left++) { - Unsafe.Add(ref fastRef, c + l) = (byte)i; + Unsafe.Add(ref lookaheadRef, huffCode + left) = (byte)i; } } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs index a2f57e485a..1c9d207cd1 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs @@ -46,9 +46,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [Theory] [WithFile(TestImages.Jpeg.Issues.InvalidJpegThrowsWrongException797, PixelTypes.Rgba32)] public void LoadingImage_InvalidTagLength_ShouldThrow(TestImageProvider provider) - where TPixel : struct, IPixel - { - Assert.Throws(() => provider.GetImage()); - } + where TPixel : struct, IPixel => Assert.Throws(() => provider.GetImage()); + + [Theory] + [WithFile(TestImages.Jpeg.Issues.AccessViolationException798, PixelTypes.Rgba32)] + public void LoadingImage_BadHuffman_ShouldNotThrow(TestImageProvider provider) + where TPixel : struct, IPixel => Assert.NotNull(provider.GetImage()); } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 460aceba0a..8d8a32fba3 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -168,6 +168,7 @@ namespace SixLabors.ImageSharp.Tests public const string ExifGetString750Transform = "Jpg/issues/issue750-exif-tranform.jpg"; public const string ExifGetString750Load = "Jpg/issues/issue750-exif-load.jpg"; public const string InvalidJpegThrowsWrongException797 = "Jpg/issues/Issue797-InvalidImage.jpg"; + public const string AccessViolationException798 = "Jpg/issues/Issue798-AccessViolationException.jpg"; } public static readonly string[] All = Baseline.All.Concat(Progressive.All).ToArray(); diff --git a/tests/Images/Input/Jpg/issues/Issue798-AccessViolationException.jpg b/tests/Images/Input/Jpg/issues/Issue798-AccessViolationException.jpg new file mode 100644 index 0000000000..6cdbfff7c5 --- /dev/null +++ b/tests/Images/Input/Jpg/issues/Issue798-AccessViolationException.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:85a9eabf5b14ff974b91ba9e9b0001eda56365903d92cb4eccc52afc7f2477ab +size 381 From bbb739ba939d13623829ad8da3a755ddf0d496db Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 18 Jan 2019 07:58:54 +1100 Subject: [PATCH 011/223] Fix Color Filters (#806) * Use Matrix5x4 and fix Hue Filter * Update test references. Fix #802 * Increase tolerance to handle xplat variance. * Rename to ColorMatrix --- src/ImageSharp/Common/Helpers/Vector4Utils.cs | 37 ++ src/ImageSharp/Primitives/ColorMatrix.cs | 459 ++++++++++++++++++ src/ImageSharp/Processing/FilterExtensions.cs | 6 +- .../Processing/KnownFilterMatrices.cs | 358 +++++++------- .../Processors/Filters/FilterProcessor.cs | 36 +- .../Formats/GeneralFormatTests.cs | 3 +- .../Helpers/Vector4UtilsTests.cs | 6 +- .../Primitives/ColorMatrixTests.cs | 270 +++++++++++ .../Processors/Filters/BrightnessTest.cs | 8 +- .../Processors/Filters/ColorBlindnessTest.cs | 9 +- .../Processors/Filters/FilterTest.cs | 20 +- .../TestUtilities/ApproximateFloatComparer.cs | 28 +- tests/Images/External | 2 +- 13 files changed, 1017 insertions(+), 225 deletions(-) create mode 100644 src/ImageSharp/Primitives/ColorMatrix.cs create mode 100644 tests/ImageSharp.Tests/Primitives/ColorMatrixTests.cs diff --git a/src/ImageSharp/Common/Helpers/Vector4Utils.cs b/src/ImageSharp/Common/Helpers/Vector4Utils.cs index 75bb00b6a5..5c122217d1 100644 --- a/src/ImageSharp/Common/Helpers/Vector4Utils.cs +++ b/src/ImageSharp/Common/Helpers/Vector4Utils.cs @@ -5,6 +5,7 @@ using System; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp { @@ -70,5 +71,41 @@ namespace SixLabors.ImageSharp UnPremultiply(ref v); } } + + /// + /// Transforms a vector by the given matrix. + /// + /// The source vector. + /// The transformation matrix. + [MethodImpl(InliningOptions.ShortMethod)] + public static void Transform(ref Vector4 vector, ref ColorMatrix matrix) + { + float x = vector.X; + float y = vector.Y; + float z = vector.Z; + float w = vector.W; + + vector.X = (x * matrix.M11) + (y * matrix.M21) + (z * matrix.M31) + (w * matrix.M41) + matrix.M51; + vector.Y = (x * matrix.M12) + (y * matrix.M22) + (z * matrix.M32) + (w * matrix.M42) + matrix.M52; + vector.Z = (x * matrix.M13) + (y * matrix.M23) + (z * matrix.M33) + (w * matrix.M43) + matrix.M53; + vector.W = (x * matrix.M14) + (y * matrix.M24) + (z * matrix.M34) + (w * matrix.M44) + matrix.M54; + } + + /// + /// Bulk variant of + /// + /// The span of vectors + /// The transformation matrix. + [MethodImpl(InliningOptions.ShortMethod)] + public static void Transform(Span vectors, ref ColorMatrix matrix) + { + ref Vector4 baseRef = ref MemoryMarshal.GetReference(vectors); + + for (int i = 0; i < vectors.Length; i++) + { + ref Vector4 v = ref Unsafe.Add(ref baseRef, i); + Transform(ref v, ref matrix); + } + } } } \ No newline at end of file diff --git a/src/ImageSharp/Primitives/ColorMatrix.cs b/src/ImageSharp/Primitives/ColorMatrix.cs new file mode 100644 index 0000000000..af2e9465a9 --- /dev/null +++ b/src/ImageSharp/Primitives/ColorMatrix.cs @@ -0,0 +1,459 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +#pragma warning disable SA1117 // Parameters should be on same line or separate lines +using System; +using System.Globalization; +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp.Primitives +{ + /// + /// A structure encapsulating a 5x4 matrix used for transforming the color and alpha components of an image. + /// + [StructLayout(LayoutKind.Sequential)] + public struct ColorMatrix : IEquatable + { + /// + /// Value at row 1, column 1 of the matrix. + /// + public float M11; + + /// + /// Value at row 1, column 2 of the matrix. + /// + public float M12; + + /// + /// Value at row 1, column 3 of the matrix. + /// + public float M13; + + /// + /// Value at row 1, column 4 of the matrix. + /// + public float M14; + + /// + /// Value at row 2, column 1 of the matrix. + /// + public float M21; + + /// + /// Value at row 2, column 2 of the matrix. + /// + public float M22; + + /// + /// Value at row 2, column 3 of the matrix. + /// + public float M23; + + /// + /// Value at row 2, column 4 of the matrix. + /// + public float M24; + + /// + /// Value at row 3, column 1 of the matrix. + /// + public float M31; + + /// + /// Value at row 3, column 2 of the matrix. + /// + public float M32; + + /// + /// Value at row 3, column 3 of the matrix. + /// + public float M33; + + /// + /// Value at row 3, column 4 of the matrix. + /// + public float M34; + + /// + /// Value at row 4, column 1 of the matrix. + /// + public float M41; + + /// + /// Value at row 4, column 2 of the matrix. + /// + public float M42; + + /// + /// Value at row 4, column 3 of the matrix. + /// + public float M43; + + /// + /// Value at row 4, column 4 of the matrix. + /// + public float M44; + + /// + /// Value at row 5, column 1 of the matrix. + /// + public float M51; + + /// + /// Value at row 5, column 2 of the matrix. + /// + public float M52; + + /// + /// Value at row 5, column 3 of the matrix. + /// + public float M53; + + /// + /// Value at row 5, column 4 of the matrix. + /// + public float M54; + + /// + /// Initializes a new instance of the struct. + /// + /// The value at row 1, column 1 of the matrix. + /// The value at row 1, column 2 of the matrix. + /// The value at row 1, column 3 of the matrix. + /// The value at row 1, column 4 of the matrix. + /// The value at row 2, column 1 of the matrix. + /// The value at row 2, column 2 of the matrix. + /// The value at row 2, column 3 of the matrix. + /// The value at row 2, column 4 of the matrix. + /// The value at row 3, column 1 of the matrix. + /// The value at row 3, column 2 of the matrix. + /// The value at row 3, column 3 of the matrix. + /// The value at row 3, column 4 of the matrix. + /// The value at row 4, column 1 of the matrix. + /// The value at row 4, column 2 of the matrix. + /// The value at row 4, column 3 of the matrix. + /// The value at row 4, column 4 of the matrix. + /// The value at row 5, column 1 of the matrix. + /// The value at row 5, column 2 of the matrix. + /// The value at row 5, column 3 of the matrix. + /// The value at row 5, column 4 of the matrix. + public ColorMatrix(float m11, float m12, float m13, float m14, + float m21, float m22, float m23, float m24, + float m31, float m32, float m33, float m34, + float m41, float m42, float m43, float m44, + float m51, float m52, float m53, float m54) + { + this.M11 = m11; + this.M12 = m12; + this.M13 = m13; + this.M14 = m14; + + this.M21 = m21; + this.M22 = m22; + this.M23 = m23; + this.M24 = m24; + + this.M31 = m31; + this.M32 = m32; + this.M33 = m33; + this.M34 = m34; + + this.M41 = m41; + this.M42 = m42; + this.M43 = m43; + this.M44 = m44; + + this.M51 = m51; + this.M52 = m52; + this.M53 = m53; + this.M54 = m54; + } + + /// + /// Gets the multiplicative identity matrix. + /// + public static ColorMatrix Identity { get; } = + new ColorMatrix(1F, 0F, 0F, 0F, + 0F, 1F, 0F, 0F, + 0F, 0F, 1F, 0F, + 0F, 0F, 0F, 1F, + 0F, 0F, 0F, 0F); + + /// + /// Gets a value indicating whether the matrix is the identity matrix. + /// + public bool IsIdentity + { + get + { + // Check diagonal element first for early out. + return this.M11 == 1F && this.M22 == 1F && this.M33 == 1F && this.M44 == 1F + && this.M12 == 0F && this.M13 == 0F && this.M14 == 0F + && this.M21 == 0F && this.M23 == 0F && this.M24 == 0F + && this.M31 == 0F && this.M32 == 0F && this.M34 == 0F + && this.M41 == 0F && this.M42 == 0F && this.M43 == 0F + && this.M51 == 0F && this.M52 == 0F && this.M53 == 0F && this.M54 == 0F; + } + } + + /// + /// Adds two matrices together. + /// + /// The first source matrix. + /// The second source matrix. + /// The resulting matrix. + public static ColorMatrix operator +(ColorMatrix value1, ColorMatrix value2) + { + ColorMatrix m; + + m.M11 = value1.M11 + value2.M11; + m.M12 = value1.M12 + value2.M12; + m.M13 = value1.M13 + value2.M13; + m.M14 = value1.M14 + value2.M14; + m.M21 = value1.M21 + value2.M21; + m.M22 = value1.M22 + value2.M22; + m.M23 = value1.M23 + value2.M23; + m.M24 = value1.M24 + value2.M24; + m.M31 = value1.M31 + value2.M31; + m.M32 = value1.M32 + value2.M32; + m.M33 = value1.M33 + value2.M33; + m.M34 = value1.M34 + value2.M34; + m.M41 = value1.M41 + value2.M41; + m.M42 = value1.M42 + value2.M42; + m.M43 = value1.M43 + value2.M43; + m.M44 = value1.M44 + value2.M44; + m.M51 = value1.M51 + value2.M51; + m.M52 = value1.M52 + value2.M52; + m.M53 = value1.M53 + value2.M53; + m.M54 = value1.M54 + value2.M54; + + return m; + } + + /// + /// Subtracts the second matrix from the first. + /// + /// The first source matrix. + /// The second source matrix. + /// The result of the subtraction. + public static ColorMatrix operator -(ColorMatrix value1, ColorMatrix value2) + { + ColorMatrix m; + + m.M11 = value1.M11 - value2.M11; + m.M12 = value1.M12 - value2.M12; + m.M13 = value1.M13 - value2.M13; + m.M14 = value1.M14 - value2.M14; + m.M21 = value1.M21 - value2.M21; + m.M22 = value1.M22 - value2.M22; + m.M23 = value1.M23 - value2.M23; + m.M24 = value1.M24 - value2.M24; + m.M31 = value1.M31 - value2.M31; + m.M32 = value1.M32 - value2.M32; + m.M33 = value1.M33 - value2.M33; + m.M34 = value1.M34 - value2.M34; + m.M41 = value1.M41 - value2.M41; + m.M42 = value1.M42 - value2.M42; + m.M43 = value1.M43 - value2.M43; + m.M44 = value1.M44 - value2.M44; + m.M51 = value1.M51 - value2.M51; + m.M52 = value1.M52 - value2.M52; + m.M53 = value1.M53 - value2.M53; + m.M54 = value1.M54 - value2.M54; + + return m; + } + + /// + /// Returns a new matrix with the negated elements of the given matrix. + /// + /// The source matrix. + /// The negated matrix. + public static unsafe ColorMatrix operator -(ColorMatrix value) + { + ColorMatrix m; + + m.M11 = -value.M11; + m.M12 = -value.M12; + m.M13 = -value.M13; + m.M14 = -value.M14; + m.M21 = -value.M21; + m.M22 = -value.M22; + m.M23 = -value.M23; + m.M24 = -value.M24; + m.M31 = -value.M31; + m.M32 = -value.M32; + m.M33 = -value.M33; + m.M34 = -value.M34; + m.M41 = -value.M41; + m.M42 = -value.M42; + m.M43 = -value.M43; + m.M44 = -value.M44; + m.M51 = -value.M51; + m.M52 = -value.M52; + m.M53 = -value.M53; + m.M54 = -value.M54; + + return m; + } + + /// + /// Multiplies a matrix by another matrix. + /// + /// The first source matrix. + /// The second source matrix. + /// The result of the multiplication. + public static ColorMatrix operator *(ColorMatrix value1, ColorMatrix value2) + { + ColorMatrix m; + + // First row + m.M11 = (value1.M11 * value2.M11) + (value1.M12 * value2.M21) + (value1.M13 * value2.M31) + (value1.M14 * value2.M41); + m.M12 = (value1.M11 * value2.M12) + (value1.M12 * value2.M22) + (value1.M13 * value2.M32) + (value1.M14 * value2.M42); + m.M13 = (value1.M11 * value2.M13) + (value1.M12 * value2.M23) + (value1.M13 * value2.M33) + (value1.M14 * value2.M43); + m.M14 = (value1.M11 * value2.M14) + (value1.M12 * value2.M24) + (value1.M13 * value2.M34) + (value1.M14 * value2.M44); + + // Second row + m.M21 = (value1.M21 * value2.M11) + (value1.M22 * value2.M21) + (value1.M23 * value2.M31) + (value1.M24 * value2.M41); + m.M22 = (value1.M21 * value2.M12) + (value1.M22 * value2.M22) + (value1.M23 * value2.M32) + (value1.M24 * value2.M42); + m.M23 = (value1.M21 * value2.M13) + (value1.M22 * value2.M23) + (value1.M23 * value2.M33) + (value1.M24 * value2.M43); + m.M24 = (value1.M21 * value2.M14) + (value1.M22 * value2.M24) + (value1.M23 * value2.M34) + (value1.M24 * value2.M44); + + // Third row + m.M31 = (value1.M31 * value2.M11) + (value1.M32 * value2.M21) + (value1.M33 * value2.M31) + (value1.M34 * value2.M41); + m.M32 = (value1.M31 * value2.M12) + (value1.M32 * value2.M22) + (value1.M33 * value2.M32) + (value1.M34 * value2.M42); + m.M33 = (value1.M31 * value2.M13) + (value1.M32 * value2.M23) + (value1.M33 * value2.M33) + (value1.M34 * value2.M43); + m.M34 = (value1.M31 * value2.M14) + (value1.M32 * value2.M24) + (value1.M33 * value2.M34) + (value1.M34 * value2.M44); + + // Fourth row + m.M41 = (value1.M41 * value2.M11) + (value1.M42 * value2.M21) + (value1.M43 * value2.M31) + (value1.M44 * value2.M41); + m.M42 = (value1.M41 * value2.M12) + (value1.M42 * value2.M22) + (value1.M43 * value2.M32) + (value1.M44 * value2.M42); + m.M43 = (value1.M41 * value2.M13) + (value1.M42 * value2.M23) + (value1.M43 * value2.M33) + (value1.M44 * value2.M43); + m.M44 = (value1.M41 * value2.M14) + (value1.M42 * value2.M24) + (value1.M43 * value2.M34) + (value1.M44 * value2.M44); + + // Fifth row + m.M51 = (value1.M51 * value2.M11) + (value1.M52 * value2.M21) + (value1.M53 * value2.M31) + (value1.M54 * value2.M41) + value2.M51; + m.M52 = (value1.M51 * value2.M12) + (value1.M52 * value2.M22) + (value1.M53 * value2.M32) + (value1.M54 * value2.M52) + value2.M52; + m.M53 = (value1.M51 * value2.M13) + (value1.M52 * value2.M23) + (value1.M53 * value2.M33) + (value1.M54 * value2.M53) + value2.M53; + m.M54 = (value1.M51 * value2.M14) + (value1.M52 * value2.M24) + (value1.M53 * value2.M34) + (value1.M54 * value2.M54) + value2.M54; + + return m; + } + + /// + /// Multiplies a matrix by a scalar value. + /// + /// The source matrix. + /// The scaling factor. + /// The scaled matrix. + public static ColorMatrix operator *(ColorMatrix value1, float value2) + { + ColorMatrix m; + + m.M11 = value1.M11 * value2; + m.M12 = value1.M12 * value2; + m.M13 = value1.M13 * value2; + m.M14 = value1.M14 * value2; + m.M21 = value1.M21 * value2; + m.M22 = value1.M22 * value2; + m.M23 = value1.M23 * value2; + m.M24 = value1.M24 * value2; + m.M31 = value1.M31 * value2; + m.M32 = value1.M32 * value2; + m.M33 = value1.M33 * value2; + m.M34 = value1.M34 * value2; + m.M41 = value1.M41 * value2; + m.M42 = value1.M42 * value2; + m.M43 = value1.M43 * value2; + m.M44 = value1.M44 * value2; + m.M51 = value1.M51 * value2; + m.M52 = value1.M52 * value2; + m.M53 = value1.M53 * value2; + m.M54 = value1.M54 * value2; + + return m; + } + + /// + /// Returns a boolean indicating whether the given two matrices are equal. + /// + /// The first matrix to compare. + /// The second matrix to compare. + /// True if the given matrices are equal; False otherwise. + public static bool operator ==(ColorMatrix value1, ColorMatrix value2) => value1.Equals(value2); + + /// + /// Returns a boolean indicating whether the given two matrices are not equal. + /// + /// The first matrix to compare. + /// The second matrix to compare. + /// True if the given matrices are equal; False otherwise. + public static bool operator !=(ColorMatrix value1, ColorMatrix value2) => !value1.Equals(value2); + + /// + public override bool Equals(object obj) => obj is ColorMatrix matrix && this.Equals(matrix); + + /// + public bool Equals(ColorMatrix other) => + this.M11 == other.M11 + && this.M12 == other.M12 + && this.M13 == other.M13 + && this.M14 == other.M14 + && this.M21 == other.M21 + && this.M22 == other.M22 + && this.M23 == other.M23 + && this.M24 == other.M24 + && this.M31 == other.M31 + && this.M32 == other.M32 + && this.M33 == other.M33 + && this.M34 == other.M34 + && this.M41 == other.M41 + && this.M42 == other.M42 + && this.M43 == other.M43 + && this.M44 == other.M44 + && this.M51 == other.M51 + && this.M52 == other.M52 + && this.M53 == other.M53 + && this.M54 == other.M54; + + /// + public override int GetHashCode() + { + HashCode hash = default; + hash.Add(this.M11); + hash.Add(this.M12); + hash.Add(this.M13); + hash.Add(this.M14); + hash.Add(this.M21); + hash.Add(this.M22); + hash.Add(this.M23); + hash.Add(this.M24); + hash.Add(this.M31); + hash.Add(this.M32); + hash.Add(this.M33); + hash.Add(this.M34); + hash.Add(this.M41); + hash.Add(this.M42); + hash.Add(this.M43); + hash.Add(this.M44); + hash.Add(this.M51); + hash.Add(this.M52); + hash.Add(this.M53); + hash.Add(this.M54); + return hash.ToHashCode(); + } + + /// + public override string ToString() + { + CultureInfo ci = CultureInfo.CurrentCulture; + + return string.Format(ci, "{{ {{M11:{0} M12:{1} M13:{2} M14:{3}}} {{M21:{4} M22:{5} M23:{6} M24:{7}}} {{M31:{8} M32:{9} M33:{10} M34:{11}}} {{M41:{12} M42:{13} M43:{14} M44:{15}}} {{M51:{16} M52:{17} M53:{18} M54:{19}}} }}", + this.M11.ToString(ci), this.M12.ToString(ci), this.M13.ToString(ci), this.M14.ToString(ci), + this.M21.ToString(ci), this.M22.ToString(ci), this.M23.ToString(ci), this.M24.ToString(ci), + this.M31.ToString(ci), this.M32.ToString(ci), this.M33.ToString(ci), this.M34.ToString(ci), + this.M41.ToString(ci), this.M42.ToString(ci), this.M43.ToString(ci), this.M44.ToString(ci), + this.M51.ToString(ci), this.M52.ToString(ci), this.M53.ToString(ci), this.M54.ToString(ci)); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/FilterExtensions.cs b/src/ImageSharp/Processing/FilterExtensions.cs index bfae4ae654..70ac232863 100644 --- a/src/ImageSharp/Processing/FilterExtensions.cs +++ b/src/ImageSharp/Processing/FilterExtensions.cs @@ -1,8 +1,8 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System.Numerics; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing.Processors.Filters; using SixLabors.Primitives; @@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Processing /// The image this method extends. /// The filter color matrix /// The . - public static IImageProcessingContext Filter(this IImageProcessingContext source, Matrix4x4 matrix) + public static IImageProcessingContext Filter(this IImageProcessingContext source, ColorMatrix matrix) where TPixel : struct, IPixel => source.ApplyProcessor(new FilterProcessor(matrix)); @@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.Processing /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext Filter(this IImageProcessingContext source, Matrix4x4 matrix, Rectangle rectangle) + public static IImageProcessingContext Filter(this IImageProcessingContext source, ColorMatrix matrix, Rectangle rectangle) where TPixel : struct, IPixel => source.ApplyProcessor(new FilterProcessor(matrix), rectangle); } diff --git a/src/ImageSharp/Processing/KnownFilterMatrices.cs b/src/ImageSharp/Processing/KnownFilterMatrices.cs index cf0d19ff85..9c725d0277 100644 --- a/src/ImageSharp/Processing/KnownFilterMatrices.cs +++ b/src/ImageSharp/Processing/KnownFilterMatrices.cs @@ -2,19 +2,28 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Numerics; - +using SixLabors.ImageSharp.Primitives; + +// Many of these matrices are tranlated from Chromium project where +// SkScalar[] is memory-mapped to a row-major matrix. +// The following translates to our column-major form: +// +// | 0| 1| 2| 3| 4| |0|5|10|15| |M11|M12|M13|M14| +// | 5| 6| 7| 8| 9| |1|6|11|16| |M21|M22|M23|M24| +// |10|11|12|13|14| = |2|7|12|17| = |M31|M32|M33|M34| +// |15|16|17|18|19| |3|8|13|18| |M41|M42|M43|M44| +// |4|9|14|19| |M51|M52|M53|M54| namespace SixLabors.ImageSharp.Processing { /// - /// A collection of known values for composing filters + /// A collection of known values for composing filters /// public static class KnownFilterMatrices { /// /// Gets a filter recreating Achromatomaly (Color desensitivity) color blindness /// - public static Matrix4x4 AchromatomalyFilter { get; } = new Matrix4x4 + public static ColorMatrix AchromatomalyFilter { get; } = new ColorMatrix { M11 = .618F, M12 = .163F, @@ -31,7 +40,7 @@ namespace SixLabors.ImageSharp.Processing /// /// Gets a filter recreating Achromatopsia (Monochrome) color blindness. /// - public static Matrix4x4 AchromatopsiaFilter { get; } = new Matrix4x4 + public static ColorMatrix AchromatopsiaFilter { get; } = new ColorMatrix { M11 = .299F, M12 = .299F, @@ -42,97 +51,97 @@ namespace SixLabors.ImageSharp.Processing M31 = .114F, M32 = .114F, M33 = .114F, - M44 = 1 + M44 = 1F }; /// /// Gets a filter recreating Deuteranomaly (Green-Weak) color blindness. /// - public static Matrix4x4 DeuteranomalyFilter { get; } = new Matrix4x4 + public static ColorMatrix DeuteranomalyFilter { get; } = new ColorMatrix { - M11 = 0.8F, - M12 = 0.258F, - M21 = 0.2F, - M22 = 0.742F, - M23 = 0.142F, - M33 = 0.858F, - M44 = 1 + M11 = .8F, + M12 = .258F, + M21 = .2F, + M22 = .742F, + M23 = .142F, + M33 = .858F, + M44 = 1F }; /// /// Gets a filter recreating Deuteranopia (Green-Blind) color blindness. /// - public static Matrix4x4 DeuteranopiaFilter { get; } = new Matrix4x4 + public static ColorMatrix DeuteranopiaFilter { get; } = new ColorMatrix { - M11 = 0.625F, - M12 = 0.7F, - M21 = 0.375F, - M22 = 0.3F, - M23 = 0.3F, - M33 = 0.7F, - M44 = 1 + M11 = .625F, + M12 = .7F, + M21 = .375F, + M22 = .3F, + M23 = .3F, + M33 = .7F, + M44 = 1F }; /// /// Gets a filter recreating Protanomaly (Red-Weak) color blindness. /// - public static Matrix4x4 ProtanomalyFilter { get; } = new Matrix4x4 + public static ColorMatrix ProtanomalyFilter { get; } = new ColorMatrix { - M11 = 0.817F, - M12 = 0.333F, - M21 = 0.183F, - M22 = 0.667F, - M23 = 0.125F, - M33 = 0.875F, - M44 = 1 + M11 = .817F, + M12 = .333F, + M21 = .183F, + M22 = .667F, + M23 = .125F, + M33 = .875F, + M44 = 1F }; /// /// Gets a filter recreating Protanopia (Red-Blind) color blindness. /// - public static Matrix4x4 ProtanopiaFilter { get; } = new Matrix4x4 + public static ColorMatrix ProtanopiaFilter { get; } = new ColorMatrix { - M11 = 0.567F, - M12 = 0.558F, - M21 = 0.433F, - M22 = 0.442F, - M23 = 0.242F, - M33 = 0.758F, - M44 = 1 + M11 = .567F, + M12 = .558F, + M21 = .433F, + M22 = .442F, + M23 = .242F, + M33 = .758F, + M44 = 1F }; /// /// Gets a filter recreating Tritanomaly (Blue-Weak) color blindness. /// - public static Matrix4x4 TritanomalyFilter { get; } = new Matrix4x4 + public static ColorMatrix TritanomalyFilter { get; } = new ColorMatrix { - M11 = 0.967F, - M21 = 0.33F, - M22 = 0.733F, - M23 = 0.183F, - M32 = 0.267F, - M33 = 0.817F, - M44 = 1 + M11 = .967F, + M21 = .33F, + M22 = .733F, + M23 = .183F, + M32 = .267F, + M33 = .817F, + M44 = 1F }; /// /// Gets a filter recreating Tritanopia (Blue-Blind) color blindness. /// - public static Matrix4x4 TritanopiaFilter { get; } = new Matrix4x4 + public static ColorMatrix TritanopiaFilter { get; } = new ColorMatrix { - M11 = 0.95F, - M21 = 0.05F, - M22 = 0.433F, - M23 = 0.475F, - M32 = 0.567F, - M33 = 0.525F, - M44 = 1 + M11 = .95F, + M21 = .05F, + M22 = .433F, + M23 = .475F, + M32 = .567F, + M33 = .525F, + M44 = 1F }; /// /// Gets an approximated black and white filter /// - public static Matrix4x4 BlackWhiteFilter { get; } = new Matrix4x4() + public static ColorMatrix BlackWhiteFilter { get; } = new ColorMatrix() { M11 = 1.5F, M12 = 1.5F, @@ -143,24 +152,24 @@ namespace SixLabors.ImageSharp.Processing M31 = 1.5F, M32 = 1.5F, M33 = 1.5F, - M41 = -1F, - M42 = -1F, - M43 = -1F, - M44 = 1 + M44 = 1F, + M51 = -1F, + M52 = -1F, + M53 = -1F, }; /// /// Gets a filter recreating an old Kodachrome camera effect. /// - public static Matrix4x4 KodachromeFilter { get; } = new Matrix4x4 + public static ColorMatrix KodachromeFilter { get; } = new ColorMatrix { - M11 = 0.7297023F, - M22 = 0.6109577F, - M33 = 0.597218F, - M41 = 0.105F, - M42 = 0.145F, - M43 = 0.155F, - M44 = 1 + M11 = .7297023F, + M22 = .6109577F, + M33 = .597218F, + M44 = 1F, + M51 = .105F, + M52 = .145F, + M53 = .155F, } * CreateSaturateFilter(1.2F) * CreateContrastFilter(1.35F); @@ -168,15 +177,15 @@ namespace SixLabors.ImageSharp.Processing /// /// Gets a filter recreating an old Lomograph camera effect. /// - public static Matrix4x4 LomographFilter { get; } = new Matrix4x4 + public static ColorMatrix LomographFilter { get; } = new ColorMatrix { M11 = 1.5F, M22 = 1.45F, M33 = 1.16F, - M41 = -.1F, - M42 = -.02F, - M43 = -.07F, - M44 = 1 + M44 = 1F, + M51 = -.1F, + M52 = -.02F, + M53 = -.07F, } * CreateSaturateFilter(1.1F) * CreateContrastFilter(1.33F); @@ -184,21 +193,21 @@ namespace SixLabors.ImageSharp.Processing /// /// Gets a filter recreating an old Polaroid camera effect. /// - public static Matrix4x4 PolaroidFilter { get; } = new Matrix4x4 + public static ColorMatrix PolaroidFilter { get; } = new ColorMatrix { M11 = 1.538F, - M12 = -0.062F, - M13 = -0.262F, - M21 = -0.022F, + M12 = -.062F, + M13 = -.262F, + M21 = -.022F, M22 = 1.578F, - M23 = -0.022F, + M23 = -.022F, M31 = .216F, M32 = -.16F, M33 = 1.5831F, - M41 = 0.02F, - M42 = -0.05F, - M43 = -0.05F, - M44 = 1 + M44 = 1F, + M51 = .02F, + M52 = -.05F, + M53 = -.05F }; /// @@ -209,18 +218,18 @@ namespace SixLabors.ImageSharp.Processing /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing brighter results. /// /// The proportion of the conversion. Must be greater than or equal to 0. - /// The - public static Matrix4x4 CreateBrightnessFilter(float amount) + /// The + public static ColorMatrix CreateBrightnessFilter(float amount) { Guard.MustBeGreaterThanOrEqualTo(amount, 0, nameof(amount)); // See https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc - return new Matrix4x4 + return new ColorMatrix { M11 = amount, M22 = amount, M33 = amount, - M44 = 1 + M44 = 1F }; } @@ -232,23 +241,23 @@ namespace SixLabors.ImageSharp.Processing /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing results with more contrast. /// /// The proportion of the conversion. Must be greater than or equal to 0. - /// The - public static Matrix4x4 CreateContrastFilter(float amount) + /// The + public static ColorMatrix CreateContrastFilter(float amount) { Guard.MustBeGreaterThanOrEqualTo(amount, 0, nameof(amount)); // See https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc float contrast = (-.5F * amount) + .5F; - return new Matrix4x4 + return new ColorMatrix { M11 = amount, M22 = amount, M33 = amount, - M41 = contrast, - M42 = contrast, - M43 = contrast, - M44 = 1 + M44 = 1F, + M51 = contrast, + M52 = contrast, + M53 = contrast }; } @@ -257,26 +266,27 @@ namespace SixLabors.ImageSharp.Processing /// /// /// The proportion of the conversion. Must be between 0 and 1. - /// The - public static Matrix4x4 CreateGrayscaleBt601Filter(float amount) + /// The + public static ColorMatrix CreateGrayscaleBt601Filter(float amount) { - Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount)); + Guard.MustBeBetweenOrEqualTo(amount, 0, 1F, nameof(amount)); amount = 1F - amount; - // https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc - return new Matrix4x4 - { - M11 = .299F + (.701F * amount), - M12 = .299F - (.299F * amount), - M13 = .299F - (.299F * amount), - M21 = .587F - (.587F * amount), - M22 = .587F + (.413F * amount), - M23 = .587F - (.587F * amount), - M31 = .114F - (.114F * amount), - M32 = .114F - (.114F * amount), - M33 = .114F + (.886F * amount), - M44 = 1 - }; + ColorMatrix m = default; + m.M11 = .299F + (.701F * amount); + m.M21 = .587F - (.587F * amount); + m.M31 = 1F - (m.M11 + m.M21); + + m.M12 = .299F - (.299F * amount); + m.M22 = .587F + (.2848F * amount); + m.M32 = 1F - (m.M12 + m.M22); + + m.M13 = .299F - (.299F * amount); + m.M23 = .587F - (.587F * amount); + m.M33 = 1F - (m.M13 + m.M23); + m.M44 = 1F; + + return m; } /// @@ -284,34 +294,36 @@ namespace SixLabors.ImageSharp.Processing /// /// /// The proportion of the conversion. Must be between 0 and 1. - /// The - public static Matrix4x4 CreateGrayscaleBt709Filter(float amount) + /// The + public static ColorMatrix CreateGrayscaleBt709Filter(float amount) { - Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount)); + Guard.MustBeBetweenOrEqualTo(amount, 0, 1F, nameof(amount)); amount = 1F - amount; // https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc - return new Matrix4x4 - { - M11 = .2126F + (.7874F * amount), - M12 = .2126F - (.2126F * amount), - M13 = .2126F - (.2126F * amount), - M21 = .7152F - (.7152F * amount), - M22 = .7152F + (.2848F * amount), - M23 = .7152F - (.7152F * amount), - M31 = .0722F - (.0722F * amount), - M32 = .0722F - (.0722F * amount), - M33 = .0722F + (.9278F * amount), - M44 = 1 - }; + ColorMatrix m = default; + m.M11 = .2126F + (.7874F * amount); + m.M21 = .7152F - (.7152F * amount); + m.M31 = 1F - (m.M11 + m.M21); + + m.M12 = .2126F - (.2126F * amount); + m.M22 = .7152F + (.2848F * amount); + m.M32 = 1F - (m.M12 + m.M22); + + m.M13 = .2126F - (.2126F * amount); + m.M23 = .7152F - (.7152F * amount); + m.M33 = 1F - (m.M13 + m.M23); + m.M44 = 1F; + + return m; } /// /// Create a hue filter matrix using the given angle in degrees. /// /// The angle of rotation in degrees. - /// The - public static Matrix4x4 CreateHueFilter(float degrees) + /// The + public static ColorMatrix CreateHueFilter(float degrees) { // Wrap the angle round at 360. degrees %= 360; @@ -329,18 +341,20 @@ namespace SixLabors.ImageSharp.Processing // The matrix is set up to preserve the luminance of the image. // See http://graficaobscura.com/matrix/index.html // Number are taken from https://msdn.microsoft.com/en-us/library/jj192162(v=vs.85).aspx - return new Matrix4x4 + return new ColorMatrix { M11 = .213F + (cosRadian * .787F) - (sinRadian * .213F), - M12 = .213F - (cosRadian * .213F) - (sinRadian * 0.143F), - M13 = .213F - (cosRadian * .213F) - (sinRadian * .787F), M21 = .715F - (cosRadian * .715F) - (sinRadian * .715F), - M22 = .715F + (cosRadian * .285F) + (sinRadian * 0.140F), - M23 = .715F - (cosRadian * .715F) + (sinRadian * .715F), M31 = .072F - (cosRadian * .072F) + (sinRadian * .928F), - M32 = .072F - (cosRadian * .072F) - (sinRadian * 0.283F), + + M12 = .213F - (cosRadian * .213F) + (sinRadian * .143F), + M22 = .715F + (cosRadian * .285F) + (sinRadian * .140F), + M32 = .072F - (cosRadian * .072F) - (sinRadian * .283F), + + M13 = .213F - (cosRadian * .213F) - (sinRadian * .787F), + M23 = .715F - (cosRadian * .715F) + (sinRadian * .715F), M33 = .072F + (cosRadian * .928F) + (sinRadian * .072F), - M44 = 1 + M44 = 1F }; } @@ -348,23 +362,23 @@ namespace SixLabors.ImageSharp.Processing /// Create an invert filter matrix using the given amount. /// /// The proportion of the conversion. Must be between 0 and 1. - /// The - public static Matrix4x4 CreateInvertFilter(float amount) + /// The + public static ColorMatrix CreateInvertFilter(float amount) { Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount)); // See https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc float invert = 1F - (2F * amount); - return new Matrix4x4 + return new ColorMatrix { M11 = invert, M22 = invert, M33 = invert, - M41 = amount, - M42 = amount, - M43 = amount, - M44 = 1 + M44 = 1F, + M51 = amount, + M52 = amount, + M53 = amount, }; } @@ -372,17 +386,17 @@ namespace SixLabors.ImageSharp.Processing /// Create an opacity filter matrix using the given amount. /// /// The proportion of the conversion. Must be between 0 and 1. - /// The - public static Matrix4x4 CreateOpacityFilter(float amount) + /// The + public static ColorMatrix CreateOpacityFilter(float amount) { Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount)); // See https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc - return new Matrix4x4 + return new ColorMatrix { - M11 = 1, - M22 = 1, - M33 = 1, + M11 = 1F, + M22 = 1F, + M33 = 1F, M44 = amount }; } @@ -395,25 +409,27 @@ namespace SixLabors.ImageSharp.Processing /// Other values are linear multipliers on the effect. Values of amount over 1 are allowed, providing super-saturated results /// /// The proportion of the conversion. Must be greater than or equal to 0. - /// The - public static Matrix4x4 CreateSaturateFilter(float amount) + /// The + public static ColorMatrix CreateSaturateFilter(float amount) { Guard.MustBeGreaterThanOrEqualTo(amount, 0, nameof(amount)); // See https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc - return new Matrix4x4 - { - M11 = .213F + (.787F * amount), - M12 = .213F - (.213F * amount), - M13 = .213F - (.213F * amount), - M21 = .715F - (.715F * amount), - M22 = .715F + (.285F * amount), - M23 = .715F - (.715F * amount), - M31 = 1F - ((.213F + (.787F * amount)) + (.715F - (.715F * amount))), - M32 = 1F - ((.213F - (.213F * amount)) + (.715F + (.285F * amount))), - M33 = 1F - ((.213F - (.213F * amount)) + (.715F - (.715F * amount))), - M44 = 1 - }; + ColorMatrix m = default; + m.M11 = .213F + (.787F * amount); + m.M21 = .715F - (.715F * amount); + m.M31 = 1F - (m.M11 + m.M21); + + m.M12 = .213F - (.213F * amount); + m.M22 = .715F + (.285F * amount); + m.M32 = 1F - (m.M12 + m.M22); + + m.M13 = .213F - (.213F * amount); + m.M23 = .715F - (.715F * amount); + m.M33 = 1F - (m.M13 + m.M23); + m.M44 = 1F; + + return m; } /// @@ -421,25 +437,27 @@ namespace SixLabors.ImageSharp.Processing /// The formula used matches the svg specification. /// /// The proportion of the conversion. Must be between 0 and 1. - /// The - public static Matrix4x4 CreateSepiaFilter(float amount) + /// The + public static ColorMatrix CreateSepiaFilter(float amount) { Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount)); amount = 1F - amount; // See https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc - return new Matrix4x4 + return new ColorMatrix { M11 = .393F + (.607F * amount), - M12 = .349F - (.349F * amount), - M13 = .272F - (.272F * amount), M21 = .769F - (.769F * amount), - M22 = .686F + (.314F * amount), - M23 = .534F - (.534F * amount), M31 = .189F - (.189F * amount), + + M12 = .349F - (.349F * amount), + M22 = .686F + (.314F * amount), M32 = .168F - (.168F * amount), + + M13 = .272F - (.272F * amount), + M23 = .534F - (.534F * amount), M33 = .131F + (.869F * amount), - M44 = 1 + M44 = 1F }; } } diff --git a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs index d3a44c066e..d6a32d8899 100644 --- a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs @@ -3,16 +3,16 @@ using System; using System.Numerics; -using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Primitives; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// - /// Provides methods that accept a matrix to apply free-form filters to images. + /// Provides methods that accept a matrix to apply free-form filters to images. /// /// The pixel format. internal class FilterProcessor : ImageProcessor @@ -22,38 +22,36 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// Initializes a new instance of the class. /// /// The matrix used to apply the image filter - public FilterProcessor(Matrix4x4 matrix) - { - this.Matrix = matrix; - } + public FilterProcessor(ColorMatrix matrix) => this.Matrix = matrix; /// - /// Gets the used to apply the image filter. + /// Gets the used to apply the image filter. /// - public Matrix4x4 Matrix { get; } + public ColorMatrix Matrix { get; } /// protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); + int startX = interest.X; - Matrix4x4 matrix = this.Matrix; + ColorMatrix matrix = this.Matrix; - ParallelHelper.IterateRows( + ParallelHelper.IterateRowsWithTempBuffer( interest, configuration, - rows => + (rows, vectorBuffer) => { for (int y = rows.Min; y < rows.Max; y++) { - Span row = source.GetPixelRowSpan(y); - - for (int x = interest.X; x < interest.Right; x++) - { - ref TPixel pixel = ref row[x]; - var vector = Vector4.Transform(pixel.ToVector4(), matrix); - pixel.FromVector4(vector); - } + Span vectorSpan = vectorBuffer.Span; + int length = vectorSpan.Length; + Span rowSpan = source.GetPixelRowSpan(y).Slice(startX, length); + PixelOperations.Instance.ToVector4(configuration, rowSpan, vectorSpan); + + Vector4Utils.Transform(vectorSpan, ref matrix); + + PixelOperations.Instance.FromVector4(configuration, vectorSpan, rowSpan); } }); } diff --git a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs index 158a085d5a..aed1edfbf0 100644 --- a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs +++ b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs @@ -57,8 +57,7 @@ namespace SixLabors.ImageSharp.Tests { using (Image image = file.CreateImage()) { - var encoder = new PngEncoder { Quantizer = new WuQuantizer(KnownDiffusers.JarvisJudiceNinke, 256), ColorType = PngColorType.Palette }; - image.Save($"{path}/{file.FileName}.png", encoder); + image.Save($"{path}/{file.FileName}"); } } } diff --git a/tests/ImageSharp.Tests/Helpers/Vector4UtilsTests.cs b/tests/ImageSharp.Tests/Helpers/Vector4UtilsTests.cs index 9416be740a..f2e98b131a 100644 --- a/tests/ImageSharp.Tests/Helpers/Vector4UtilsTests.cs +++ b/tests/ImageSharp.Tests/Helpers/Vector4UtilsTests.cs @@ -11,6 +11,8 @@ namespace SixLabors.ImageSharp.Tests.Helpers { public class Vector4UtilsTests { + private readonly ApproximateFloatComparer ApproximateFloatComparer = new ApproximateFloatComparer(1e-6f); + [Theory] [InlineData(0)] [InlineData(1)] @@ -23,7 +25,7 @@ namespace SixLabors.ImageSharp.Tests.Helpers Vector4Utils.Premultiply(source); - Assert.Equal(expected, source, new ApproximateFloatComparer(1e-6f)); + Assert.Equal(expected, source, this.ApproximateFloatComparer); } [Theory] @@ -38,7 +40,7 @@ namespace SixLabors.ImageSharp.Tests.Helpers Vector4Utils.UnPremultiply(source); - Assert.Equal(expected, source, new ApproximateFloatComparer(1e-6f)); + Assert.Equal(expected, source, this.ApproximateFloatComparer); } } } diff --git a/tests/ImageSharp.Tests/Primitives/ColorMatrixTests.cs b/tests/ImageSharp.Tests/Primitives/ColorMatrixTests.cs new file mode 100644 index 0000000000..2fbe260ecd --- /dev/null +++ b/tests/ImageSharp.Tests/Primitives/ColorMatrixTests.cs @@ -0,0 +1,270 @@ +using System; +using System.Globalization; +using SixLabors.ImageSharp.Primitives; +using SixLabors.ImageSharp.Processing; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Primitives +{ + public class ColorMatrixTests + { + private readonly ApproximateFloatComparer ApproximateFloatComparer = new ApproximateFloatComparer(1e-6f); + + [Fact] + public void ColorMatrixIdentityIsCorrect() + { + ColorMatrix val = default; + val.M11 = val.M22 = val.M33 = val.M44 = 1F; + + Assert.Equal(val, ColorMatrix.Identity, this.ApproximateFloatComparer); + } + + [Fact] + public void ColorMatrixCanDetectIdentity() + { + ColorMatrix m = ColorMatrix.Identity; + Assert.True(m.IsIdentity); + + m.M12 = 1F; + Assert.False(m.IsIdentity); + } + + [Fact] + public void ColorMatrixEquality() + { + ColorMatrix m = KnownFilterMatrices.CreateHueFilter(45F); + ColorMatrix m2 = KnownFilterMatrices.CreateHueFilter(45F); + object obj = m2; + + Assert.True(m.Equals(obj)); + Assert.True(m.Equals(m2)); + Assert.True(m == m2); + Assert.False(m != m2); + } + + [Fact] + public void ColorMatrixMultiply() + { + ColorMatrix value1 = this.CreateAllTwos(); + ColorMatrix value2 = this.CreateAllThrees(); + + ColorMatrix m; + + // First row + m.M11 = (value1.M11 * value2.M11) + (value1.M12 * value2.M21) + (value1.M13 * value2.M31) + (value1.M14 * value2.M41); + m.M12 = (value1.M11 * value2.M12) + (value1.M12 * value2.M22) + (value1.M13 * value2.M32) + (value1.M14 * value2.M42); + m.M13 = (value1.M11 * value2.M13) + (value1.M12 * value2.M23) + (value1.M13 * value2.M33) + (value1.M14 * value2.M43); + m.M14 = (value1.M11 * value2.M14) + (value1.M12 * value2.M24) + (value1.M13 * value2.M34) + (value1.M14 * value2.M44); + + // Second row + m.M21 = (value1.M21 * value2.M11) + (value1.M22 * value2.M21) + (value1.M23 * value2.M31) + (value1.M24 * value2.M41); + m.M22 = (value1.M21 * value2.M12) + (value1.M22 * value2.M22) + (value1.M23 * value2.M32) + (value1.M24 * value2.M42); + m.M23 = (value1.M21 * value2.M13) + (value1.M22 * value2.M23) + (value1.M23 * value2.M33) + (value1.M24 * value2.M43); + m.M24 = (value1.M21 * value2.M14) + (value1.M22 * value2.M24) + (value1.M23 * value2.M34) + (value1.M24 * value2.M44); + + // Third row + m.M31 = (value1.M31 * value2.M11) + (value1.M32 * value2.M21) + (value1.M33 * value2.M31) + (value1.M34 * value2.M41); + m.M32 = (value1.M31 * value2.M12) + (value1.M32 * value2.M22) + (value1.M33 * value2.M32) + (value1.M34 * value2.M42); + m.M33 = (value1.M31 * value2.M13) + (value1.M32 * value2.M23) + (value1.M33 * value2.M33) + (value1.M34 * value2.M43); + m.M34 = (value1.M31 * value2.M14) + (value1.M32 * value2.M24) + (value1.M33 * value2.M34) + (value1.M34 * value2.M44); + + // Fourth row + m.M41 = (value1.M41 * value2.M11) + (value1.M42 * value2.M21) + (value1.M43 * value2.M31) + (value1.M44 * value2.M41); + m.M42 = (value1.M41 * value2.M12) + (value1.M42 * value2.M22) + (value1.M43 * value2.M32) + (value1.M44 * value2.M42); + m.M43 = (value1.M41 * value2.M13) + (value1.M42 * value2.M23) + (value1.M43 * value2.M33) + (value1.M44 * value2.M43); + m.M44 = (value1.M41 * value2.M14) + (value1.M42 * value2.M24) + (value1.M43 * value2.M34) + (value1.M44 * value2.M44); + + // Fifth row + m.M51 = (value1.M51 * value2.M11) + (value1.M52 * value2.M21) + (value1.M53 * value2.M31) + (value1.M54 * value2.M41) + value2.M51; + m.M52 = (value1.M51 * value2.M12) + (value1.M52 * value2.M22) + (value1.M53 * value2.M32) + (value1.M54 * value2.M52) + value2.M52; + m.M53 = (value1.M51 * value2.M13) + (value1.M52 * value2.M23) + (value1.M53 * value2.M33) + (value1.M54 * value2.M53) + value2.M53; + m.M54 = (value1.M51 * value2.M14) + (value1.M52 * value2.M24) + (value1.M53 * value2.M34) + (value1.M54 * value2.M54) + value2.M54; + + Assert.Equal(m, value1 * value2, this.ApproximateFloatComparer); + } + + [Fact] + public void ColorMatrixMultiplyScalar() + { + ColorMatrix m = this.CreateAllTwos(); + Assert.Equal(this.CreateAllFours(), m * 2, this.ApproximateFloatComparer); + } + + [Fact] + public void ColorMatrixSubtract() + { + ColorMatrix m = this.CreateAllOnes() + this.CreateAllTwos(); + Assert.Equal(this.CreateAllThrees(), m); + } + + [Fact] + public void ColorMatrixNegate() + { + ColorMatrix m = this.CreateAllOnes() * -1F; + Assert.Equal(m, -this.CreateAllOnes()); + } + + [Fact] + public void ColorMatrixAdd() + { + ColorMatrix m = this.CreateAllOnes() + this.CreateAllTwos(); + Assert.Equal(this.CreateAllThrees(), m); + } + + [Fact] + public void ColorMatrixHashCode() + { +#if NETCOREAPP2_1 + ColorMatrix m = KnownFilterMatrices.CreateBrightnessFilter(.5F); + HashCode hash = default; + hash.Add(m.M11); + hash.Add(m.M12); + hash.Add(m.M13); + hash.Add(m.M14); + hash.Add(m.M21); + hash.Add(m.M22); + hash.Add(m.M23); + hash.Add(m.M24); + hash.Add(m.M31); + hash.Add(m.M32); + hash.Add(m.M33); + hash.Add(m.M34); + hash.Add(m.M41); + hash.Add(m.M42); + hash.Add(m.M43); + hash.Add(m.M44); + hash.Add(m.M51); + hash.Add(m.M52); + hash.Add(m.M53); + hash.Add(m.M54); + + Assert.Equal(hash.ToHashCode(), m.GetHashCode()); +#endif + } + + [Fact] + public void ColorMatrixToString() + { + ColorMatrix m = KnownFilterMatrices.CreateBrightnessFilter(.5F); + + CultureInfo ci = CultureInfo.CurrentCulture; + + string expected = string.Format(ci, "{{ {{M11:{0} M12:{1} M13:{2} M14:{3}}} {{M21:{4} M22:{5} M23:{6} M24:{7}}} {{M31:{8} M32:{9} M33:{10} M34:{11}}} {{M41:{12} M42:{13} M43:{14} M44:{15}}} {{M51:{16} M52:{17} M53:{18} M54:{19}}} }}", + m.M11.ToString(ci), m.M12.ToString(ci), m.M13.ToString(ci), m.M14.ToString(ci), + m.M21.ToString(ci), m.M22.ToString(ci), m.M23.ToString(ci), m.M24.ToString(ci), + m.M31.ToString(ci), m.M32.ToString(ci), m.M33.ToString(ci), m.M34.ToString(ci), + m.M41.ToString(ci), m.M42.ToString(ci), m.M43.ToString(ci), m.M44.ToString(ci), + m.M51.ToString(ci), m.M52.ToString(ci), m.M53.ToString(ci), m.M54.ToString(ci)); + + Assert.Equal(expected, m.ToString()); + } + + private ColorMatrix CreateAllOnes() + { + return new ColorMatrix + { + M11 = 1F, + M12 = 1F, + M13 = 1F, + M14 = 1F, + M21 = 1F, + M22 = 1F, + M23 = 1F, + M24 = 1F, + M31 = 1F, + M32 = 1F, + M33 = 1F, + M34 = 1F, + M41 = 1F, + M42 = 1F, + M43 = 1F, + M44 = 1F, + M51 = 1F, + M52 = 1F, + M53 = 1F, + M54 = 1F + }; + } + + private ColorMatrix CreateAllTwos() + { + return new ColorMatrix + { + M11 = 2F, + M12 = 2F, + M13 = 2F, + M14 = 2F, + M21 = 2F, + M22 = 2F, + M23 = 2F, + M24 = 2F, + M31 = 2F, + M32 = 2F, + M33 = 2F, + M34 = 2F, + M41 = 2F, + M42 = 2F, + M43 = 2F, + M44 = 2F, + M51 = 2F, + M52 = 2F, + M53 = 2F, + M54 = 2F + }; + } + + private ColorMatrix CreateAllThrees() + { + return new ColorMatrix + { + M11 = 3F, + M12 = 3F, + M13 = 3F, + M14 = 3F, + M21 = 3F, + M22 = 3F, + M23 = 3F, + M24 = 3F, + M31 = 3F, + M32 = 3F, + M33 = 3F, + M34 = 3F, + M41 = 3F, + M42 = 3F, + M43 = 3F, + M44 = 3F, + M51 = 3F, + M52 = 3F, + M53 = 3F, + M54 = 3F + }; + } + + private ColorMatrix CreateAllFours() + { + return new ColorMatrix + { + M11 = 4F, + M12 = 4F, + M13 = 4F, + M14 = 4F, + M21 = 4F, + M22 = 4F, + M23 = 4F, + M24 = 4F, + M31 = 4F, + M32 = 4F, + M33 = 4F, + M34 = 4F, + M41 = 4F, + M42 = 4F, + M43 = 4F, + M44 = 4F, + M51 = 4F, + M52 = 4F, + M53 = 4F, + M54 = 4F + }; + } + } +} diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/BrightnessTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/BrightnessTest.cs index ed790cbac8..54a8dd4b7d 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Filters/BrightnessTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/BrightnessTest.cs @@ -8,10 +8,13 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects { using SixLabors.ImageSharp.Processing; + using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; [GroupOutput("Filters")] public class BrightnessTest { + private readonly ImageComparer imageComparer = ImageComparer.Tolerant(0.007F); + public static readonly TheoryData BrightnessValues = new TheoryData { @@ -22,9 +25,6 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects [Theory] [WithTestPatternImages(nameof(BrightnessValues), 48, 48, PixelTypes.Rgba32)] public void ApplyBrightnessFilter(TestImageProvider provider, float value) - where TPixel : struct, IPixel - { - provider.RunValidatingProcessorTest(ctx => ctx.Brightness(value), value); - } + where TPixel : struct, IPixel => provider.RunValidatingProcessorTest(ctx => ctx.Brightness(value), value, this.imageComparer); } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/ColorBlindnessTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/ColorBlindnessTest.cs index 3d48e16ec9..8ac56655ea 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Filters/ColorBlindnessTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/ColorBlindnessTest.cs @@ -2,17 +2,19 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters { using SixLabors.ImageSharp.Processing; + using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; [GroupOutput("Filters")] public class ColorBlindnessTest { + private readonly ImageComparer imageComparer = ImageComparer.Tolerant(0.03F); + public static readonly TheoryData ColorBlindnessFilters = new TheoryData { @@ -29,9 +31,6 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters [Theory] [WithTestPatternImages(nameof(ColorBlindnessFilters), 48, 48, PixelTypes.Rgba32)] public void ApplyColorBlindnessFilter(TestImageProvider provider, ColorBlindnessMode colorBlindness) - where TPixel : struct, IPixel - { - provider.RunValidatingProcessorTest(x => x.ColorBlindness(colorBlindness), colorBlindness.ToString()); - } + where TPixel : struct, IPixel => provider.RunValidatingProcessorTest(x => x.ColorBlindness(colorBlindness), colorBlindness.ToString(), this.imageComparer); } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/FilterTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/FilterTest.cs index 479a3c33a2..68daa80eac 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Filters/FilterTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/FilterTest.cs @@ -1,16 +1,13 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System.Numerics; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; - -using SixLabors.Primitives; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters { + using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing; [GroupOutput("Filters")] @@ -25,7 +22,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters public void ApplyFilter(TestImageProvider provider) where TPixel : struct, IPixel { - Matrix4x4 m = CreateCombinedTestFilterMatrix(); + ColorMatrix m = CreateCombinedTestFilterMatrix(); provider.RunValidatingProcessorTest(x => x.Filter(m), comparer: ValidatorComparer); } @@ -35,18 +32,17 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters public void ApplyFilterInBox(TestImageProvider provider) where TPixel : struct, IPixel { - Matrix4x4 m = CreateCombinedTestFilterMatrix(); + ColorMatrix m = CreateCombinedTestFilterMatrix(); provider.RunRectangleConstrainedValidatingProcessorTest((x, b) => x.Filter(m, b), comparer: ValidatorComparer); } - private static Matrix4x4 CreateCombinedTestFilterMatrix() + private static ColorMatrix CreateCombinedTestFilterMatrix() { - Matrix4x4 brightness = KnownFilterMatrices.CreateBrightnessFilter(0.9F); - Matrix4x4 hue = KnownFilterMatrices.CreateHueFilter(180F); - Matrix4x4 saturation = KnownFilterMatrices.CreateSaturateFilter(1.5F); - Matrix4x4 m = brightness * hue * saturation; - return m; + ColorMatrix brightness = KnownFilterMatrices.CreateBrightnessFilter(0.9F); + ColorMatrix hue = KnownFilterMatrices.CreateHueFilter(180F); + ColorMatrix saturation = KnownFilterMatrices.CreateSaturateFilter(1.5F); + return brightness * hue * saturation; } } diff --git a/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs b/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs index 47ca6cccb2..872a935ffe 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Numerics; +using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Tests { @@ -11,8 +12,9 @@ namespace SixLabors.ImageSharp.Tests /// internal readonly struct ApproximateFloatComparer : IEqualityComparer, + IEqualityComparer, IEqualityComparer, - IEqualityComparer + IEqualityComparer { private readonly float Epsilon; @@ -20,7 +22,7 @@ namespace SixLabors.ImageSharp.Tests /// Initializes a new instance of the class. /// /// The comparison error difference epsilon to use. - public ApproximateFloatComparer(float epsilon = 1f) => this.Epsilon = epsilon; + public ApproximateFloatComparer(float epsilon = 1F) => this.Epsilon = epsilon; /// public bool Equals(float x, float y) @@ -34,17 +36,29 @@ namespace SixLabors.ImageSharp.Tests public int GetHashCode(float obj) => obj.GetHashCode(); /// - public bool Equals(Vector4 a, Vector4 b) => this.Equals(a.X, b.X) && this.Equals(a.Y, b.Y) && this.Equals(a.Z, b.Z) && this.Equals(a.W, b.W); + public bool Equals(Vector2 x, Vector2 y) => this.Equals(x.X, y.X) && this.Equals(x.Y, y.Y); /// - public int GetHashCode(Vector4 obj) => obj.GetHashCode(); + public int GetHashCode(Vector2 obj) => obj.GetHashCode(); + + /// + public bool Equals(Vector4 x, Vector4 y) => this.Equals(x.X, y.X) && this.Equals(x.Y, y.Y) && this.Equals(x.Z, y.Z) && this.Equals(x.W, y.W); /// - public bool Equals(Vector2 a, Vector2 b) => this.Equals(a.X, b.X) && this.Equals(a.Y, b.Y); + public int GetHashCode(Vector4 obj) => obj.GetHashCode(); - public int GetHashCode(Vector2 obj) + /// + public bool Equals(ColorMatrix x, ColorMatrix y) { - throw new System.NotImplementedException(); + return + this.Equals(x.M11, y.M11) && this.Equals(x.M12, y.M12) && this.Equals(x.M13, y.M13) && this.Equals(x.M14, y.M14) + && this.Equals(x.M21, y.M21) && this.Equals(x.M22, y.M22) && this.Equals(x.M23, y.M23) && this.Equals(x.M24, y.M24) + && this.Equals(x.M31, y.M31) && this.Equals(x.M32, y.M32) && this.Equals(x.M33, y.M33) && this.Equals(x.M34, y.M34) + && this.Equals(x.M41, y.M41) && this.Equals(x.M42, y.M42) && this.Equals(x.M43, y.M43) && this.Equals(x.M44, y.M44) + && this.Equals(x.M51, y.M51) && this.Equals(x.M52, y.M52) && this.Equals(x.M53, y.M53) && this.Equals(x.M54, y.M54); } + + /// + public int GetHashCode(ColorMatrix obj) => obj.GetHashCode(); } } \ No newline at end of file diff --git a/tests/Images/External b/tests/Images/External index 7ada45bc34..74995302e3 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 7ada45bc3484f40e28a50817386ca93f293acd11 +Subproject commit 74995302e3c9a5913f1cf09d71b15f888d0ec022 From 84ba4bbf0d20d711b0d65e6983bfec165f220388 Mon Sep 17 00:00:00 2001 From: Brian Popow <38701097+brianpopow@users.noreply.github.com> Date: Fri, 18 Jan 2019 08:11:55 +0100 Subject: [PATCH 012/223] Decoding Bitmaps with BITFIELDS masks (#796) * decoding bitmaps with Bitfields masks * added testcases for Bitfields bitmaps * added parsing of the complete bitmap V4 header to get the color masks infos for the BITFIELDS compression * writing now explicitly a Bitamp v3 header (40 bytes) * added test image for issue #735 * fixed rescaling 5-bit / 6-bit to 0 - 255 range * BitmapEncoder now writes BMP v4 header * using MemoryMarshal.Cast to simplify parsing v4 header * added testcases for bitmap v3, v4, v5 * Bitmap encoder writes again V3 header instead of V4. Additional fields for V4 are zero anyway. * added parsing of special case for 56 bytes headers * using the alpha mask in ReadRgb32BitFields() when its present * added support for bitmasks greater then 8 bits per channel * using MagickReferenceDecoder reference decoder for the test with 10 bits pixel masks * changed bitmap constants to hex * added enum for the bitmap info header type * added support for 52 bytes header (same as 56 bytes without the alpha mask) * clarified comment with difference between imagesharp and magick decoder for Rgba321010102 * BmpEncoder now writes a V4 info header and uses BITFIELDS compression * workaround for issue that the decoder does not decode the alpha channel correctly: For V3 bitmaps, the alpha channel will be ignored during encoding * Fix #732 --- src/ImageSharp/Formats/Bmp/BmpCompression.cs | 2 - src/ImageSharp/Formats/Bmp/BmpConstants.cs | 36 ++ src/ImageSharp/Formats/Bmp/BmpDecoder.cs | 2 - src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 399 ++++++++++++++++-- src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs | 50 ++- src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs | 252 ++++++++++- .../Formats/Bmp/BmpInfoHeaderType.cs | 48 +++ src/ImageSharp/Formats/Bmp/BmpMetaData.cs | 11 +- .../Formats/Bmp/BmpDecoderTests.cs | 107 ++++- tests/ImageSharp.Tests/TestImages.cs | 29 +- tests/Images/Input/Bmp/issue735.bmp | 3 + tests/Images/Input/Bmp/pal8v4.bmp | 3 + tests/Images/Input/Bmp/pal8v5.bmp | 3 + tests/Images/Input/Bmp/rgb16-565.bmp | 3 + tests/Images/Input/Bmp/rgb16-565pal.bmp | 3 + tests/Images/Input/Bmp/rgb16bfdef.bmp | 3 + tests/Images/Input/Bmp/rgb24.bmp | 3 + tests/Images/Input/Bmp/rgb32bf.bmp | 3 + tests/Images/Input/Bmp/rgb32bfdef.bmp | 3 + tests/Images/Input/Bmp/rgba32-1010102.bmp | 3 + tests/Images/Input/Bmp/rgba32.bmp | 3 + tests/Images/Input/Bmp/rgba32h56.bmp | 3 + 22 files changed, 895 insertions(+), 77 deletions(-) create mode 100644 src/ImageSharp/Formats/Bmp/BmpInfoHeaderType.cs create mode 100644 tests/Images/Input/Bmp/issue735.bmp create mode 100644 tests/Images/Input/Bmp/pal8v4.bmp create mode 100644 tests/Images/Input/Bmp/pal8v5.bmp create mode 100644 tests/Images/Input/Bmp/rgb16-565.bmp create mode 100644 tests/Images/Input/Bmp/rgb16-565pal.bmp create mode 100644 tests/Images/Input/Bmp/rgb16bfdef.bmp create mode 100644 tests/Images/Input/Bmp/rgb24.bmp create mode 100644 tests/Images/Input/Bmp/rgb32bf.bmp create mode 100644 tests/Images/Input/Bmp/rgb32bfdef.bmp create mode 100644 tests/Images/Input/Bmp/rgba32-1010102.bmp create mode 100644 tests/Images/Input/Bmp/rgba32.bmp create mode 100644 tests/Images/Input/Bmp/rgba32h56.bmp diff --git a/src/ImageSharp/Formats/Bmp/BmpCompression.cs b/src/ImageSharp/Formats/Bmp/BmpCompression.cs index ef063f0106..5f14d22436 100644 --- a/src/ImageSharp/Formats/Bmp/BmpCompression.cs +++ b/src/ImageSharp/Formats/Bmp/BmpCompression.cs @@ -25,7 +25,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// If the first byte is zero, the record has different meanings, depending /// on the second byte. If the second byte is zero, it is the end of the row, /// if it is one, it is the end of the image. - /// Not supported at the moment. /// RLE8 = 1, @@ -42,7 +41,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// /// Each image row has a multiple of four elements. If the /// row has less elements, zeros will be added at the right side. - /// Not supported at the moment. /// BitFields = 3, diff --git a/src/ImageSharp/Formats/Bmp/BmpConstants.cs b/src/ImageSharp/Formats/Bmp/BmpConstants.cs index 99799b619c..5cbed4af2b 100644 --- a/src/ImageSharp/Formats/Bmp/BmpConstants.cs +++ b/src/ImageSharp/Formats/Bmp/BmpConstants.cs @@ -19,5 +19,41 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// The list of file extensions that equate to a bmp. /// public static readonly IEnumerable FileExtensions = new[] { "bm", "bmp", "dip" }; + + /// + /// Valid magic bytes markers identifying a Bitmap file. + /// + internal static class TypeMarkers + { + /// + /// Single-image BMP file that may have been created under Windows or OS/2. + /// + public const int Bitmap = 0x4D42; + + /// + /// OS/2 Bitmap Array. + /// + public const int BitmapArray = 0x4142; + + /// + /// OS/2 Color Icon. + /// + public const int ColorIcon = 0x4943; + + /// + /// OS/2 Color Pointer. + /// + public const int ColorPointer = 0x5043; + + /// + /// OS/2 Icon. + /// + public const int Icon = 0x4349; + + /// + /// OS/2 Pointer. + /// + public const int Pointer = 0x5450; + } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs index 3d079cf619..82af2a671e 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs @@ -15,8 +15,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// JPG /// PNG /// RLE4 - /// RLE8 - /// BitFields /// /// Formats will be supported in a later releases. We advise always /// to use only 24 Bit Windows bitmaps. diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 68528edcd6..362fe64430 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -2,8 +2,10 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.Buffers.Binary; using System.IO; +using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Memory; @@ -14,7 +16,7 @@ using SixLabors.Memory; namespace SixLabors.ImageSharp.Formats.Bmp { /// - /// Performs the bmp decoding operation. + /// Performs the bitmap decoding operation. /// /// /// A useful decoding source example can be found at @@ -22,19 +24,19 @@ namespace SixLabors.ImageSharp.Formats.Bmp internal sealed class BmpDecoderCore { /// - /// The mask for the red part of the color for 16 bit rgb bitmaps. + /// The default mask for the red part of the color for 16 bit rgb bitmaps. /// - private const int Rgb16RMask = 0x7C00; + private const int DefaultRgb16RMask = 0x7C00; /// - /// The mask for the green part of the color for 16 bit rgb bitmaps. + /// The default mask for the green part of the color for 16 bit rgb bitmaps. /// - private const int Rgb16GMask = 0x3E0; + private const int DefaultRgb16GMask = 0x3E0; /// - /// The mask for the blue part of the color for 16 bit rgb bitmaps. + /// The default mask for the blue part of the color for 16 bit rgb bitmaps. /// - private const int Rgb16BMask = 0x1F; + private const int DefaultRgb16BMask = 0x1F; /// /// RLE8 flag value that indicates following byte has special meaning. @@ -62,10 +64,15 @@ namespace SixLabors.ImageSharp.Formats.Bmp private Stream stream; /// - /// The metadata + /// The metadata. /// private ImageMetaData metaData; + /// + /// The bmp specific metadata. + /// + private BmpMetaData bmpMetaData; + /// /// The file header containing general information. /// TODO: Why is this not used? We advance the stream but do not use the values parsed. @@ -85,7 +92,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// Initializes a new instance of the class. /// /// The configuration. - /// The options + /// The options. public BmpDecoderCore(Configuration configuration, IBmpDecoderOptions options) { this.configuration = configuration; @@ -119,7 +126,14 @@ namespace SixLabors.ImageSharp.Formats.Bmp case BmpCompression.RGB: if (this.infoHeader.BitsPerPixel == 32) { - this.ReadRgb32(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); + if (this.bmpMetaData.InfoHeaderType == BmpInfoHeaderType.WinVersion3) + { + this.ReadRgb32Slow(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); + } + else + { + this.ReadRgb32Fast(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); + } } else if (this.infoHeader.BitsPerPixel == 24) { @@ -146,6 +160,12 @@ namespace SixLabors.ImageSharp.Formats.Bmp this.ReadRle8(pixels, palette, this.infoHeader.Width, this.infoHeader.Height, inverted); break; + + case BmpCompression.BitFields: + this.ReadBitFields(pixels, inverted); + + break; + default: throw new NotSupportedException("Does not support this kind of bitmap files."); } @@ -199,12 +219,39 @@ namespace SixLabors.ImageSharp.Formats.Bmp } /// - /// Performs final shifting from a 5bit value to an 8bit one. + /// Decodes a bitmap containing BITFIELDS Compression type. For each color channel, there will be bitmask + /// which will be used to determine which bits belong to that channel. /// - /// The masked and shifted value - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static byte GetBytesFrom5BitValue(int value) => (byte)((value << 3) | (value >> 2)); + /// The pixel format. + /// The output pixel buffer containing the decoded image. + /// Whether the bitmap is inverted. + private void ReadBitFields(Buffer2D pixels, bool inverted) + where TPixel : struct, IPixel + { + if (this.infoHeader.BitsPerPixel == 16) + { + this.ReadRgb16( + pixels, + this.infoHeader.Width, + this.infoHeader.Height, + inverted, + this.infoHeader.RedMask, + this.infoHeader.GreenMask, + this.infoHeader.BlueMask); + } + else + { + this.ReadRgb32BitFields( + pixels, + this.infoHeader.Width, + this.infoHeader.Height, + inverted, + this.infoHeader.RedMask, + this.infoHeader.GreenMask, + this.infoHeader.BlueMask, + this.infoHeader.AlphaMask); + } + } /// /// Looks up color values and builds the image from de-compressed RLE8 data. @@ -240,12 +287,12 @@ namespace SixLabors.ImageSharp.Formats.Bmp } /// - /// Produce uncompressed bitmap data from RLE8 stream + /// Produce uncompressed bitmap data from RLE8 stream. /// /// - /// RLE8 is a 2-byte run-length encoding - ///
If first byte is 0, the second byte may have special meaning - ///
Otherwise, first byte is the length of the run and second byte is the color for the run + /// RLE8 is a 2-byte run-length encoding. + ///
If first byte is 0, the second byte may have special meaning. + ///
Otherwise, first byte is the length of the run and second byte is the color for the run. ///
/// The width of the bitmap. /// Buffer for uncompressed data. @@ -382,20 +429,32 @@ namespace SixLabors.ImageSharp.Formats.Bmp } /// - /// Reads the 16 bit color palette from the stream + /// Reads the 16 bit color palette from the stream. /// /// The pixel format. /// The to assign the palette to. /// The width of the bitmap. /// The height of the bitmap. /// Whether the bitmap is inverted. - private void ReadRgb16(Buffer2D pixels, int width, int height, bool inverted) + /// The bitmask for the red channel. + /// The bitmask for the green channel. + /// The bitmask for the blue channel. + private void ReadRgb16(Buffer2D pixels, int width, int height, bool inverted, int redMask = DefaultRgb16RMask, int greenMask = DefaultRgb16GMask, int blueMask = DefaultRgb16BMask) where TPixel : struct, IPixel { int padding = CalculatePadding(width, 2); int stride = (width * 2) + padding; TPixel color = default; + int rightShiftRedMask = CalculateRightShift((uint)redMask); + int rightShiftGreenMask = CalculateRightShift((uint)greenMask); + int rightShiftBlueMask = CalculateRightShift((uint)blueMask); + + // Each color channel contains either 5 or 6 Bits values. + int redMaskBits = CountBits((uint)redMask); + int greenMaskBits = CountBits((uint)greenMask); + int blueMaskBits = CountBits((uint)blueMask); + using (IManagedByteBuffer buffer = this.memoryAllocator.AllocateManagedByteBuffer(stride)) { for (int y = 0; y < height; y++) @@ -409,10 +468,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp { short temp = BitConverter.ToInt16(buffer.Array, offset); - var rgb = new Rgb24( - GetBytesFrom5BitValue((temp & Rgb16RMask) >> 10), - GetBytesFrom5BitValue((temp & Rgb16GMask) >> 5), - GetBytesFrom5BitValue(temp & Rgb16BMask)); + // Rescale values, so the values range from 0 to 255. + int r = (redMaskBits == 5) ? GetBytesFrom5BitValue((temp & redMask) >> rightShiftRedMask) : GetBytesFrom6BitValue((temp & redMask) >> rightShiftRedMask); + int g = (greenMaskBits == 5) ? GetBytesFrom5BitValue((temp & greenMask) >> rightShiftGreenMask) : GetBytesFrom6BitValue((temp & greenMask) >> rightShiftGreenMask); + int b = (blueMaskBits == 5) ? GetBytesFrom5BitValue((temp & blueMask) >> rightShiftBlueMask) : GetBytesFrom6BitValue((temp & blueMask) >> rightShiftBlueMask); + var rgb = new Rgb24((byte)r, (byte)g, (byte)b); color.FromRgb24(rgb); pixelRow[x] = color; @@ -423,7 +483,23 @@ namespace SixLabors.ImageSharp.Formats.Bmp } /// - /// Reads the 24 bit color palette from the stream + /// Performs final shifting from a 5bit value to an 8bit one. + /// + /// The masked and shifted value. + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static byte GetBytesFrom5BitValue(int value) => (byte)((value << 3) | (value >> 2)); + + /// + /// Performs final shifting from a 6bit value to an 8bit one. + /// + /// The masked and shifted value. + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static byte GetBytesFrom6BitValue(int value) => (byte)((value << 2) | (value >> 4)); + + /// + /// Reads the 24 bit color palette from the stream. /// /// The pixel format. /// The to assign the palette to. @@ -452,14 +528,14 @@ namespace SixLabors.ImageSharp.Formats.Bmp } /// - /// Reads the 32 bit color palette from the stream + /// Reads the 32 bit color palette from the stream. /// /// The pixel format. /// The to assign the palette to. /// The width of the bitmap. /// The height of the bitmap. /// Whether the bitmap is inverted. - private void ReadRgb32(Buffer2D pixels, int width, int height, bool inverted) + private void ReadRgb32Fast(Buffer2D pixels, int width, int height, bool inverted) where TPixel : struct, IPixel { int padding = CalculatePadding(width, 4); @@ -480,6 +556,228 @@ namespace SixLabors.ImageSharp.Formats.Bmp } } + /// + /// Reads the 32 bit color palette from the stream, checking the alpha component of each pixel. + /// This is a special case only used for 32bpp WinBMPv3 files, which could be in either BGR0 or BGRA format. + /// + /// The pixel format. + /// The to assign the palette to. + /// The width of the bitmap. + /// The height of the bitmap. + /// Whether the bitmap is inverted. + private void ReadRgb32Slow(Buffer2D pixels, int width, int height, bool inverted) + where TPixel : struct, IPixel + { + int padding = CalculatePadding(width, 4); + + using (IManagedByteBuffer row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 4, padding)) + using (IMemoryOwner bgraRow = this.memoryAllocator.Allocate(width)) + { + Span bgraRowSpan = bgraRow.GetSpan(); + long currentPosition = this.stream.Position; + bool hasAlpha = false; + + // Loop though the rows checking each pixel. We start by assuming it's + // an BGR0 image. If we hit a non-zero alpha value, then we know it's + // actually a BGRA image, and change tactics accordingly. + for (int y = 0; y < height; y++) + { + this.stream.Read(row); + + PixelOperations.Instance.FromBgra32Bytes( + this.configuration, + row.GetSpan(), + bgraRowSpan, + width); + + // Check each pixel in the row to see if it has an alpha value. + for (int x = 0; x < width; x++) + { + Bgra32 bgra = bgraRowSpan[x]; + if (bgra.A > 0) + { + hasAlpha = true; + break; + } + } + + if (hasAlpha) + { + break; + } + } + + // Reset our stream for a second pass. + this.stream.Position = currentPosition; + + // Process the pixels in bulk taking the raw alpha component value. + if (hasAlpha) + { + for (int y = 0; y < height; y++) + { + this.stream.Read(row); + + int newY = Invert(y, height, inverted); + Span pixelSpan = pixels.GetRowSpan(newY); + + PixelOperations.Instance.FromBgra32Bytes( + this.configuration, + row.GetSpan(), + pixelSpan, + width); + } + + return; + } + + // Slow path. We need to set each alpha component value to fully opaque. + for (int y = 0; y < height; y++) + { + this.stream.Read(row); + PixelOperations.Instance.FromBgra32Bytes( + this.configuration, + row.GetSpan(), + bgraRowSpan, + width); + + int newY = Invert(y, height, inverted); + Span pixelSpan = pixels.GetRowSpan(newY); + + for (int x = 0; x < width; x++) + { + Bgra32 bgra = bgraRowSpan[x]; + bgra.A = byte.MaxValue; + ref TPixel pixel = ref pixelSpan[x]; + pixel.FromBgra32(bgra); + } + } + } + } + + /// + /// Decode an 32 Bit Bitmap containing a bitmask for each color channel. + /// + /// The pixel format. + /// The output pixel buffer containing the decoded image. + /// The width of the image. + /// The height of the image. + /// Whether the bitmap is inverted. + /// The bitmask for the red channel. + /// The bitmask for the green channel. + /// The bitmask for the blue channel. + /// The bitmask for the alpha channel. + private void ReadRgb32BitFields(Buffer2D pixels, int width, int height, bool inverted, int redMask, int greenMask, int blueMask, int alphaMask) + where TPixel : struct, IPixel + { + TPixel color = default; + int padding = CalculatePadding(width, 4); + int stride = (width * 4) + padding; + + int rightShiftRedMask = CalculateRightShift((uint)redMask); + int rightShiftGreenMask = CalculateRightShift((uint)greenMask); + int rightShiftBlueMask = CalculateRightShift((uint)blueMask); + int rightShiftAlphaMask = CalculateRightShift((uint)alphaMask); + + int bitsRedMask = CountBits((uint)redMask); + int bitsGreenMask = CountBits((uint)greenMask); + int bitsBlueMask = CountBits((uint)blueMask); + int bitsAlphaMask = CountBits((uint)alphaMask); + float invMaxValueRed = 1.0f / (0xFFFFFFFF >> (32 - bitsRedMask)); + float invMaxValueGreen = 1.0f / (0xFFFFFFFF >> (32 - bitsGreenMask)); + float invMaxValueBlue = 1.0f / (0xFFFFFFFF >> (32 - bitsBlueMask)); + uint maxValueAlpha = 0xFFFFFFFF >> (32 - bitsAlphaMask); + float invMaxValueAlpha = 1.0f / maxValueAlpha; + + bool unusualBitMask = false; + if (bitsRedMask > 8 || bitsGreenMask > 8 || bitsBlueMask > 8 || invMaxValueAlpha > 8) + { + unusualBitMask = true; + } + + using (IManagedByteBuffer buffer = this.memoryAllocator.AllocateManagedByteBuffer(stride)) + { + for (int y = 0; y < height; y++) + { + this.stream.Read(buffer.Array, 0, stride); + int newY = Invert(y, height, inverted); + Span pixelRow = pixels.GetRowSpan(newY); + + int offset = 0; + for (int x = 0; x < width; x++) + { + uint temp = BitConverter.ToUInt32(buffer.Array, offset); + + if (unusualBitMask) + { + uint r = (uint)(temp & redMask) >> rightShiftRedMask; + uint g = (uint)(temp & greenMask) >> rightShiftGreenMask; + uint b = (uint)(temp & blueMask) >> rightShiftBlueMask; + float alpha = alphaMask != 0 ? invMaxValueAlpha * ((uint)(temp & alphaMask) >> rightShiftAlphaMask) : 1.0f; + var vector4 = new Vector4( + r * invMaxValueRed, + g * invMaxValueGreen, + b * invMaxValueBlue, + alpha); + color.FromVector4(vector4); + } + else + { + byte r = (byte)((temp & redMask) >> rightShiftRedMask); + byte g = (byte)((temp & greenMask) >> rightShiftGreenMask); + byte b = (byte)((temp & blueMask) >> rightShiftBlueMask); + byte a = alphaMask != 0 ? (byte)((temp & alphaMask) >> rightShiftAlphaMask) : (byte)255; + color.FromRgba32(new Rgba32(r, g, b, a)); + } + + pixelRow[x] = color; + offset += 4; + } + } + } + } + + /// + /// Calculates the necessary right shifts for a given color bitmask (the 0 bits to the right). + /// + /// The color bit mask. + /// Number of bits to shift right. + private static int CalculateRightShift(uint n) + { + int count = 0; + while (n > 0) + { + if ((1 & n) == 0) + { + count++; + } + else + { + break; + } + + n >>= 1; + } + + return count; + } + + /// + /// Counts none zero bits. + /// + /// A color mask. + /// The none zero bits. + private static int CountBits(uint n) + { + int count = 0; + while (n != 0) + { + count++; + n &= n - 1; + } + + return count; + } + /// /// Reads the from the stream. /// @@ -508,20 +806,54 @@ namespace SixLabors.ImageSharp.Formats.Bmp // read the rest of the header this.stream.Read(buffer, BmpInfoHeader.HeaderSizeSize, headerSize - BmpInfoHeader.HeaderSizeSize); + BmpInfoHeaderType inofHeaderType = BmpInfoHeaderType.WinVersion2; if (headerSize == BmpInfoHeader.CoreSize) { // 12 bytes + inofHeaderType = BmpInfoHeaderType.WinVersion2; this.infoHeader = BmpInfoHeader.ParseCore(buffer); } else if (headerSize == BmpInfoHeader.Os22ShortSize) { // 16 bytes + inofHeaderType = BmpInfoHeaderType.Os2Version2Short; this.infoHeader = BmpInfoHeader.ParseOs22Short(buffer); } - else if (headerSize >= BmpInfoHeader.Size) + else if (headerSize == BmpInfoHeader.SizeV3) + { + // == 40 bytes + inofHeaderType = BmpInfoHeaderType.WinVersion3; + this.infoHeader = BmpInfoHeader.ParseV3(buffer); + + // if the info header is BMP version 3 and the compression type is BITFIELDS, + // color masks for each color channel follow the info header. + if (this.infoHeader.Compression == BmpCompression.BitFields) + { + byte[] bitfieldsBuffer = new byte[12]; + this.stream.Read(bitfieldsBuffer, 0, 12); + Span data = bitfieldsBuffer.AsSpan(); + this.infoHeader.RedMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4)); + this.infoHeader.GreenMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)); + this.infoHeader.BlueMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)); + } + } + else if (headerSize == BmpInfoHeader.AdobeV3Size) + { + // == 52 bytes + inofHeaderType = BmpInfoHeaderType.AdobeVersion3; + this.infoHeader = BmpInfoHeader.ParseAdobeV3(buffer, withAlpha: false); + } + else if (headerSize == BmpInfoHeader.AdobeV3WithAlphaSize) + { + // == 56 bytes + inofHeaderType = BmpInfoHeaderType.AdobeVersion3WithAlpha; + this.infoHeader = BmpInfoHeader.ParseAdobeV3(buffer, withAlpha: true); + } + else if (headerSize >= BmpInfoHeader.SizeV4) { - // >= 40 bytes - this.infoHeader = BmpInfoHeader.Parse(buffer); + // >= 108 bytes + inofHeaderType = headerSize == BmpInfoHeader.SizeV4 ? BmpInfoHeaderType.WinVersion4 : BmpInfoHeaderType.WinVersion5; + this.infoHeader = BmpInfoHeader.ParseV4(buffer); } else { @@ -548,13 +880,14 @@ namespace SixLabors.ImageSharp.Formats.Bmp this.metaData = meta; short bitsPerPixel = this.infoHeader.BitsPerPixel; - BmpMetaData bmpMetaData = this.metaData.GetFormatMetaData(BmpFormat.Instance); + this.bmpMetaData = this.metaData.GetFormatMetaData(BmpFormat.Instance); + this.bmpMetaData.InfoHeaderType = inofHeaderType; // We can only encode at these bit rates so far. if (bitsPerPixel.Equals((short)BmpBitsPerPixel.Pixel24) || bitsPerPixel.Equals((short)BmpBitsPerPixel.Pixel32)) { - bmpMetaData.BitsPerPixel = (BmpBitsPerPixel)bitsPerPixel; + this.bmpMetaData.BitsPerPixel = (BmpBitsPerPixel)bitsPerPixel; } // skip the remaining header because we can't read those parts diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index a67c581eb0..27a38bc0d1 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -23,6 +23,26 @@ namespace SixLabors.ImageSharp.Formats.Bmp ///
private int padding; + /// + /// The mask for the alpha channel of the color for a 32 bit rgba bitmaps. + /// + private const int Rgba32AlphaMask = 0xFF << 24; + + /// + /// The mask for the red part of the color for a 32 bit rgba bitmaps. + /// + private const int Rgba32RedMask = 0xFF << 16; + + /// + /// The mask for the green part of the color for a 32 bit rgba bitmaps. + /// + private const int Rgba32GreenMask = 0xFF << 8; + + /// + /// The mask for the blue part of the color for a 32 bit rgba bitmaps. + /// + private const int Rgba32BlueMask = 0xFF; + private readonly MemoryAllocator memoryAllocator; private Configuration configuration; @@ -92,8 +112,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp } } + int infoHeaderSize = BmpInfoHeader.SizeV4; var infoHeader = new BmpInfoHeader( - headerSize: BmpInfoHeader.Size, + headerSize: infoHeaderSize, height: image.Height, width: image.Width, bitsPerPixel: bpp, @@ -102,26 +123,37 @@ namespace SixLabors.ImageSharp.Formats.Bmp clrUsed: 0, clrImportant: 0, xPelsPerMeter: hResolution, - yPelsPerMeter: vResolution); + yPelsPerMeter: vResolution) + { + RedMask = Rgba32RedMask, + GreenMask = Rgba32GreenMask, + BlueMask = Rgba32BlueMask, + Compression = BmpCompression.BitFields + }; + + if (this.bitsPerPixel == BmpBitsPerPixel.Pixel32) + { + infoHeader.AlphaMask = Rgba32AlphaMask; + } var fileHeader = new BmpFileHeader( - type: 19778, // BM - fileSize: 54 + infoHeader.ImageSize, + type: BmpConstants.TypeMarkers.Bitmap, + fileSize: BmpFileHeader.Size + infoHeaderSize + infoHeader.ImageSize, reserved: 0, - offset: 54); + offset: BmpFileHeader.Size + infoHeaderSize); #if NETCOREAPP2_1 - Span buffer = stackalloc byte[40]; + Span buffer = stackalloc byte[infoHeaderSize]; #else - byte[] buffer = new byte[40]; + byte[] buffer = new byte[infoHeaderSize]; #endif fileHeader.WriteTo(buffer); stream.Write(buffer, 0, BmpFileHeader.Size); - infoHeader.WriteTo(buffer); + infoHeader.WriteV4Header(buffer); - stream.Write(buffer, 0, 40); + stream.Write(buffer, 0, infoHeaderSize); this.WriteImage(stream, image.Frames.RootFrame); diff --git a/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs b/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs index 5177bc325c..316df4acc9 100644 --- a/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs +++ b/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs @@ -16,11 +16,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp [StructLayout(LayoutKind.Sequential, Pack = 1)] internal struct BmpInfoHeader { - /// - /// Defines the size of the BITMAPINFOHEADER data structure in the bitmap file. - /// - public const int Size = 40; - /// /// Defines the size of the BITMAPCOREHEADER data structure in the bitmap file. /// @@ -31,10 +26,30 @@ namespace SixLabors.ImageSharp.Formats.Bmp ///
public const int Os22ShortSize = 16; + /// + /// Defines the size of the BITMAPINFOHEADER (BMP Version 3) data structure in the bitmap file. + /// + public const int SizeV3 = 40; + + /// + /// Special case of the BITMAPINFOHEADER V3 used by adobe where the color bitmasks are part of the info header instead of following it. + /// + public const int AdobeV3Size = 52; + + /// + /// Special case of the BITMAPINFOHEADER V3 used by adobe where the color bitmasks (including the alpha channel) are part of the info header instead of following it. + /// + public const int AdobeV3WithAlphaSize = 56; + + /// + /// Defines the size of the BITMAPINFOHEADER (BMP Version 4) data structure in the bitmap file. + /// + public const int SizeV4 = 108; + /// /// Defines the size of the biggest supported header data structure in the bitmap file. /// - public const int MaxHeaderSize = Size; + public const int MaxHeaderSize = SizeV4; /// /// Defines the size of the field. @@ -52,7 +67,24 @@ namespace SixLabors.ImageSharp.Formats.Bmp int xPelsPerMeter = 0, int yPelsPerMeter = 0, int clrUsed = 0, - int clrImportant = 0) + int clrImportant = 0, + int redMask = 0, + int greenMask = 0, + int blueMask = 0, + int alphaMask = 0, + int csType = 0, + int redX = 0, + int redY = 0, + int redZ = 0, + int greenX = 0, + int greenY = 0, + int greenZ = 0, + int blueX = 0, + int blueY = 0, + int blueZ = 0, + int gammeRed = 0, + int gammeGreen = 0, + int gammeBlue = 0) { this.HeaderSize = headerSize; this.Width = width; @@ -65,6 +97,23 @@ namespace SixLabors.ImageSharp.Formats.Bmp this.YPelsPerMeter = yPelsPerMeter; this.ClrUsed = clrUsed; this.ClrImportant = clrImportant; + this.RedMask = redMask; + this.GreenMask = greenMask; + this.BlueMask = blueMask; + this.AlphaMask = alphaMask; + this.CsType = csType; + this.RedX = redX; + this.RedY = redY; + this.RedZ = redZ; + this.GreenX = greenX; + this.GreenY = greenY; + this.GreenZ = greenZ; + this.BlueX = blueX; + this.BlueY = blueY; + this.BlueZ = blueZ; + this.GammaRed = gammeRed; + this.GammaGreen = gammeGreen; + this.GammaBlue = gammeBlue; } /// @@ -130,23 +179,92 @@ namespace SixLabors.ImageSharp.Formats.Bmp public int ClrImportant { get; set; } /// - /// Parses the full BITMAPINFOHEADER header (40 bytes). + /// Gets or sets red color mask. This is used with the BITFIELDS decoding. /// - /// The data to parse. - /// Parsed header - /// - public static BmpInfoHeader Parse(ReadOnlySpan data) - { - if (data.Length != Size) - { - throw new ArgumentException(nameof(data), $"Must be {Size} bytes. Was {data.Length} bytes."); - } + public int RedMask { get; set; } - return MemoryMarshal.Cast(data)[0]; - } + /// + /// Gets or sets green color mask. This is used with the BITFIELDS decoding. + /// + public int GreenMask { get; set; } + + /// + /// Gets or sets blue color mask. This is used with the BITFIELDS decoding. + /// + public int BlueMask { get; set; } + + /// + /// Gets or sets alpha color mask. This is not used yet. + /// + public int AlphaMask { get; set; } + + /// + /// Gets or sets the Color space type. Not used yet. + /// + public int CsType { get; set; } + + /// + /// Gets or sets the X coordinate of red endpoint. Not used yet. + /// + public int RedX { get; set; } + + /// + /// Gets or sets the Y coordinate of red endpoint. Not used yet. + /// + public int RedY { get; set; } + + /// + /// Gets or sets the Z coordinate of red endpoint. Not used yet. + /// + public int RedZ { get; set; } + + /// + /// Gets or sets the X coordinate of green endpoint. Not used yet. + /// + public int GreenX { get; set; } + + /// + /// Gets or sets the Y coordinate of green endpoint. Not used yet. + /// + public int GreenY { get; set; } + + /// + /// Gets or sets the Z coordinate of green endpoint. Not used yet. + /// + public int GreenZ { get; set; } + + /// + /// Gets or sets the X coordinate of blue endpoint. Not used yet. + /// + public int BlueX { get; set; } /// - /// Parses the BITMAPCOREHEADER consisting of the headerSize, width, height, planes, and bitsPerPixel fields (12 bytes). + /// Gets or sets the Y coordinate of blue endpoint. Not used yet. + /// + public int BlueY { get; set; } + + /// + /// Gets or sets the Z coordinate of blue endpoint. Not used yet. + /// + public int BlueZ { get; set; } + + /// + /// Gets or sets the Gamma red coordinate scale value. Not used yet. + /// + public int GammaRed { get; set; } + + /// + /// Gets or sets the Gamma green coordinate scale value. Not used yet. + /// + public int GammaGreen { get; set; } + + /// + /// Gets or sets the Gamma blue coordinate scale value. Not used yet. + /// + public int GammaBlue { get; set; } + + /// + /// Parses the BITMAPCOREHEADER (BMP Version 2) consisting of the headerSize, width, height, planes, and bitsPerPixel fields (12 bytes). /// /// The data to parse. /// Parsed header @@ -163,7 +281,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// /// Parses a short variant of the OS22XBITMAPHEADER. It is identical to the BITMAPCOREHEADER, except that the width and height - /// are 4 bytes instead of 2. + /// are 4 bytes instead of 2, resulting in 16 bytes total. /// /// The data to parse. /// Parsed header @@ -178,7 +296,97 @@ namespace SixLabors.ImageSharp.Formats.Bmp bitsPerPixel: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(14, 2))); } - public unsafe void WriteTo(Span buffer) + /// + /// Parses the full BMP Version 3 BITMAPINFOHEADER header (40 bytes). + /// + /// The data to parse. + /// The parsed header. + /// + public static BmpInfoHeader ParseV3(ReadOnlySpan data) + { + return new BmpInfoHeader( + headerSize: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4)), + width: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)), + height: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)), + planes: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(12, 2)), + bitsPerPixel: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(14, 2)), + compression: (BmpCompression)BinaryPrimitives.ReadInt32LittleEndian(data.Slice(16, 4)), + imageSize: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(20, 4)), + xPelsPerMeter: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(24, 4)), + yPelsPerMeter: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(28, 4)), + clrUsed: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(32, 4)), + clrImportant: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(36, 4))); + } + + /// + /// Special case of the BITMAPINFOHEADER V3 used by adobe where the color bitmasks are part of the info header instead of following it. + /// 52 bytes without the alpha mask, 56 bytes with the alpha mask. + /// + /// The data to parse. + /// Indicates, if the alpha bitmask is present. + /// The parsed header. + /// + public static BmpInfoHeader ParseAdobeV3(ReadOnlySpan data, bool withAlpha = true) + { + return new BmpInfoHeader( + headerSize: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4)), + width: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)), + height: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)), + planes: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(12, 2)), + bitsPerPixel: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(14, 2)), + compression: (BmpCompression)BinaryPrimitives.ReadInt32LittleEndian(data.Slice(16, 4)), + imageSize: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(20, 4)), + xPelsPerMeter: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(24, 4)), + yPelsPerMeter: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(28, 4)), + clrUsed: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(32, 4)), + clrImportant: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(36, 4)), + redMask: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(40, 4)), + greenMask: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(44, 4)), + blueMask: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(48, 4)), + alphaMask: withAlpha ? BinaryPrimitives.ReadInt32LittleEndian(data.Slice(52, 4)) : 0); + } + + /// + /// Parses the full BMP Version 4 BITMAPINFOHEADER header (108 bytes). + /// + /// The data to parse. + /// The parsed header. + /// + public static BmpInfoHeader ParseV4(ReadOnlySpan data) + { + if (data.Length != SizeV4) + { + throw new ArgumentException(nameof(data), $"Must be {SizeV4} bytes. Was {data.Length} bytes."); + } + + return MemoryMarshal.Cast(data)[0]; + } + + /// + /// Writes a bitmap version 3 (Microsoft Windows NT) header to a buffer (40 bytes). + /// + /// The buffer to write to. + public void WriteV3Header(Span buffer) + { + buffer.Clear(); + BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(0, 4), SizeV3); + BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(4, 4), this.Width); + BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(8, 4), this.Height); + BinaryPrimitives.WriteInt16LittleEndian(buffer.Slice(12, 2), this.Planes); + BinaryPrimitives.WriteInt16LittleEndian(buffer.Slice(14, 2), this.BitsPerPixel); + BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(16, 4), (int)this.Compression); + BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(20, 4), this.ImageSize); + BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(24, 4), this.XPelsPerMeter); + BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(28, 4), this.YPelsPerMeter); + BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(32, 4), this.ClrUsed); + BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(36, 4), this.ClrImportant); + } + + /// + /// Writes a complete Bitmap V4 header to a buffer. + /// + /// The buffer to write to. + public unsafe void WriteV4Header(Span buffer) { ref BmpInfoHeader dest = ref Unsafe.As(ref MemoryMarshal.GetReference(buffer)); diff --git a/src/ImageSharp/Formats/Bmp/BmpInfoHeaderType.cs b/src/ImageSharp/Formats/Bmp/BmpInfoHeaderType.cs new file mode 100644 index 0000000000..a4ce0115f9 --- /dev/null +++ b/src/ImageSharp/Formats/Bmp/BmpInfoHeaderType.cs @@ -0,0 +1,48 @@ +namespace SixLabors.ImageSharp.Formats.Bmp +{ + /// + /// Enum value for the different bitmap info header types. The enum value is the number of bytes for the specific bitmap header. + /// + public enum BmpInfoHeaderType + { + /// + /// Bitmap Core or BMP Version 2 header (Microsoft Windows 2.x). + /// + WinVersion2 = 12, + + /// + /// Short variant of the OS/2 Version 2 bitmap header. + /// + Os2Version2Short = 16, + + /// + /// BMP Version 3 header (Microsoft Windows 3.x or Microsoft Windows NT). + /// + WinVersion3 = 40, + + /// + /// Adobe variant of the BMP Version 3 header. + /// + AdobeVersion3 = 52, + + /// + /// Adobe variant of the BMP Version 3 header with an alpha mask. + /// + AdobeVersion3WithAlpha = 56, + + /// + /// BMP Version 2.x header (IBM OS/2 2.x). + /// + Os2Version2 = 64, + + /// + /// BMP Version 4 header (Microsoft Windows 95). + /// + WinVersion4 = 108, + + /// + /// BMP Version 5 header (Windows NT 5.0, 98 or later). + /// + WinVersion5 = 124, + } +} diff --git a/src/ImageSharp/Formats/Bmp/BmpMetaData.cs b/src/ImageSharp/Formats/Bmp/BmpMetaData.cs index 8b33e30fa6..2d86856177 100644 --- a/src/ImageSharp/Formats/Bmp/BmpMetaData.cs +++ b/src/ImageSharp/Formats/Bmp/BmpMetaData.cs @@ -19,7 +19,16 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// Initializes a new instance of the class. /// /// The metadata to create an instance from. - private BmpMetaData(BmpMetaData other) => this.BitsPerPixel = other.BitsPerPixel; + private BmpMetaData(BmpMetaData other) + { + this.BitsPerPixel = other.BitsPerPixel; + this.InfoHeaderType = other.InfoHeaderType; + } + + /// + /// Gets or sets the bitmap info header type. + /// + public BmpInfoHeaderType InfoHeaderType { get; set; } /// /// Gets or sets the number of bits per pixel. diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index 251475567f..60e45ae35f 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -4,6 +4,9 @@ using System.IO; using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; +using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; + using Xunit; // ReSharper disable InconsistentNaming @@ -19,6 +22,8 @@ namespace SixLabors.ImageSharp.Tests public static readonly string[] AllBmpFiles = All; + public static readonly string[] BitfieldsBmpFiles = BitFields; + public static readonly TheoryData RatioFiles = new TheoryData { @@ -34,7 +39,7 @@ namespace SixLabors.ImageSharp.Tests { using (Image image = provider.GetImage(new BmpDecoder())) { - image.DebugSave(provider, "bmp"); + image.DebugSave(provider); if (TestEnvironment.IsWindows) { @@ -44,17 +49,47 @@ namespace SixLabors.ImageSharp.Tests } [Theory] - [WithFile(F, CommonNonDefaultPixelTypes)] - public void BmpDecoder_IsNotBoundToSinglePixelType(TestImageProvider provider) + [WithFileCollection(nameof(BitfieldsBmpFiles), PixelTypes.Rgba32)] + public void BmpDecoder_CanDecodeBitfields(TestImageProvider provider) where TPixel : struct, IPixel { using (Image image = provider.GetImage(new BmpDecoder())) { - image.DebugSave(provider, "bmp"); + image.DebugSave(provider); image.CompareToOriginal(provider); } } + [Theory] + [WithFile(Bit32Rgba, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecodeBitmap_WithAlphaChannel(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder())) + { + image.DebugSave(provider); + image.CompareToOriginal(provider, new MagickReferenceDecoder()); + } + } + + [Theory] + [WithFile(Rgba321010102, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecodeBitfields_WithUnusualBitmasks(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder())) + { + image.DebugSave(provider); + + // Choosing large tolerance of 6.1 here, because for some reason with the MagickReferenceDecoder the alpha channel + // seems to be wrong. This bitmap has an alpha channel of two bits. In many cases this alpha channel has a value of 3, + // which should be remapped to 255 for RGBA32, but the magick decoder has a value of 191 set. + // The total difference without the alpha channel is still: 0.0204% + // Exporting the image as PNG with GIMP yields to the same result as the imagesharp implementation. + image.CompareToOriginal(provider, ImageComparer.TolerantPercentage(6.1f), new MagickReferenceDecoder()); + } + } + [Theory] [WithFile(WinBmpv2, PixelTypes.Rgba32)] public void BmpDecoder_CanDecodeBmpv2(TestImageProvider provider) @@ -62,7 +97,67 @@ namespace SixLabors.ImageSharp.Tests { using (Image image = provider.GetImage(new BmpDecoder())) { - image.DebugSave(provider, "png"); + image.DebugSave(provider); + image.CompareToOriginal(provider); + } + } + + [Theory] + [WithFile(WinBmpv3, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecodeBmpv3(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder())) + { + image.DebugSave(provider); + image.CompareToOriginal(provider); + } + } + + [Theory] + [WithFile(Rgba32bf56, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecodeAdobeBmpv3(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder())) + { + image.DebugSave(provider); + image.CompareToOriginal(provider, new MagickReferenceDecoder()); + } + } + + [Theory] + [WithFile(WinBmpv4, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecodeBmpv4(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder())) + { + image.DebugSave(provider); + image.CompareToOriginal(provider); + } + } + + [Theory] + [WithFile(WinBmpv5, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecodeBmpv5(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder())) + { + image.DebugSave(provider); + image.CompareToOriginal(provider); + } + } + + [Theory] + [WithFile(F, CommonNonDefaultPixelTypes)] + public void BmpDecoder_IsNotBoundToSinglePixelType(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder())) + { + image.DebugSave(provider); image.CompareToOriginal(provider); } } @@ -74,7 +169,7 @@ namespace SixLabors.ImageSharp.Tests { using (Image image = provider.GetImage(new BmpDecoder())) { - image.DebugSave(provider, "png"); + image.DebugSave(provider); image.CompareToOriginal(provider); } } diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 8d8a32fba3..5ecc266572 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -205,12 +205,36 @@ namespace SixLabors.ImageSharp.Tests public const string Bit16 = "Bmp/test16.bmp"; public const string Bit16Inverted = "Bmp/test16-inverted.bmp"; public const string Bit32Rgb = "Bmp/rgb32.bmp"; - + public const string Bit32Rgba = "Bmp/rgba32.bmp"; + // Note: This format can be called OS/2 BMPv1, or Windows BMPv2 public const string WinBmpv2 = "Bmp/pal8os2v1_winv2.bmp"; + public const string WinBmpv3 = "Bmp/rgb24.bmp"; + public const string WinBmpv4 = "Bmp/pal8v4.bmp"; + public const string WinBmpv5 = "Bmp/pal8v5.bmp"; public const string Bit8Palette4 = "Bmp/pal8-0.bmp"; public const string Os2v2Short = "Bmp/pal8os2v2-16.bmp"; + // Bitmap images with compression type BITFIELDS + public const string Rgb32bfdef = "Bmp/rgb32bfdef.bmp"; + public const string Rgb32bf = "Bmp/rgb32bf.bmp"; + public const string Rgb16565 = "Bmp/rgb16-565.bmp"; + public const string Rgb16bfdef = "Bmp/rgb16bfdef.bmp"; + public const string Rgb16565pal = "Bmp/rgb16-565pal.bmp"; + public const string Issue735 = "Bmp/issue735.bmp"; + public const string Rgba32bf56 = "Bmp/rgba32h56.bmp"; + public const string Rgba321010102 = "Bmp/rgba32-1010102.bmp"; + + public static readonly string[] BitFields + = { + Rgb32bfdef, + Rgb32bf, + Rgb16565, + Rgb16bfdef, + Rgb16565pal, + Issue735, + }; + public static readonly string[] All = { Car, @@ -225,7 +249,8 @@ namespace SixLabors.ImageSharp.Tests Bit8, Bit8Inverted, Bit16, - Bit16Inverted + Bit16Inverted, + Bit32Rgb }; } diff --git a/tests/Images/Input/Bmp/issue735.bmp b/tests/Images/Input/Bmp/issue735.bmp new file mode 100644 index 0000000000..139e73830f --- /dev/null +++ b/tests/Images/Input/Bmp/issue735.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2d4b076196ca2559581b1e64f448b3dcefc78ba38a6f91953b2d7cfa95465d11 +size 1334298 diff --git a/tests/Images/Input/Bmp/pal8v4.bmp b/tests/Images/Input/Bmp/pal8v4.bmp new file mode 100644 index 0000000000..87dad63bc1 --- /dev/null +++ b/tests/Images/Input/Bmp/pal8v4.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4dc4a9725f4c7b7b8ea1e181d1aee9355fe724dbd949ab562b3b364df9c90d64 +size 9322 diff --git a/tests/Images/Input/Bmp/pal8v5.bmp b/tests/Images/Input/Bmp/pal8v5.bmp new file mode 100644 index 0000000000..82c8b0ed78 --- /dev/null +++ b/tests/Images/Input/Bmp/pal8v5.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:65ac4a579189d2fc30b3499b8246f30127e032f35d6cde90335dafe87a1b23c3 +size 9338 diff --git a/tests/Images/Input/Bmp/rgb16-565.bmp b/tests/Images/Input/Bmp/rgb16-565.bmp new file mode 100644 index 0000000000..d0d65bdbc7 --- /dev/null +++ b/tests/Images/Input/Bmp/rgb16-565.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c2ffadac9c1239fb397834415c7b5f85d5c6044bd9c31fa66e23056a19b82b1d +size 16450 diff --git a/tests/Images/Input/Bmp/rgb16-565pal.bmp b/tests/Images/Input/Bmp/rgb16-565pal.bmp new file mode 100644 index 0000000000..5bf1691622 --- /dev/null +++ b/tests/Images/Input/Bmp/rgb16-565pal.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bcf485398db9c92235e5e9a7c20aaf3899ecf00ccdf25680f85e0797df610ea4 +size 17474 diff --git a/tests/Images/Input/Bmp/rgb16bfdef.bmp b/tests/Images/Input/Bmp/rgb16bfdef.bmp new file mode 100644 index 0000000000..5967db9286 --- /dev/null +++ b/tests/Images/Input/Bmp/rgb16bfdef.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3c5d1d67482b769d875b552e6012b80434880f763c9d56a45a94d07bbbe16fba +size 16450 diff --git a/tests/Images/Input/Bmp/rgb24.bmp b/tests/Images/Input/Bmp/rgb24.bmp new file mode 100644 index 0000000000..2f54e2d5a9 --- /dev/null +++ b/tests/Images/Input/Bmp/rgb24.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a9c4fbfbf8cb6df8d2d9d1484359d037aebd25078b21137bfd6c69739fcbe2e1 +size 24630 diff --git a/tests/Images/Input/Bmp/rgb32bf.bmp b/tests/Images/Input/Bmp/rgb32bf.bmp new file mode 100644 index 0000000000..dc6d38f8d8 --- /dev/null +++ b/tests/Images/Input/Bmp/rgb32bf.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4c1189e3a039b1e5aae6347024684937c495a5736a3c309dc7c2e3c161f89751 +size 32578 diff --git a/tests/Images/Input/Bmp/rgb32bfdef.bmp b/tests/Images/Input/Bmp/rgb32bfdef.bmp new file mode 100644 index 0000000000..64598caf94 --- /dev/null +++ b/tests/Images/Input/Bmp/rgb32bfdef.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:54d89660058b6ade17f54d0664b67ced375bbd67db9b89a4ada3addeaab7381a +size 32578 diff --git a/tests/Images/Input/Bmp/rgba32-1010102.bmp b/tests/Images/Input/Bmp/rgba32-1010102.bmp new file mode 100644 index 0000000000..2eb610cbfc --- /dev/null +++ b/tests/Images/Input/Bmp/rgba32-1010102.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3a784719de2d33f8678ea0efb8b52be662e0f126e723150010057cf8fd89e777 +size 32650 diff --git a/tests/Images/Input/Bmp/rgba32.bmp b/tests/Images/Input/Bmp/rgba32.bmp new file mode 100644 index 0000000000..e331b25bd8 --- /dev/null +++ b/tests/Images/Input/Bmp/rgba32.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:257e0127fa0ccdb6eb8381338dda0fb692b2ea4d38775f1aed56dfdccaed2ab4 +size 32650 diff --git a/tests/Images/Input/Bmp/rgba32h56.bmp b/tests/Images/Input/Bmp/rgba32h56.bmp new file mode 100644 index 0000000000..9efaa4e4e0 --- /dev/null +++ b/tests/Images/Input/Bmp/rgba32h56.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:12d76fd71a341c80a80a5c0116dea8d7338fe801d8483b594124d7b20ed1d782 +size 32582 From 0d68e6d09d23ce3c6fa6fbf9fe48af1ece53490e Mon Sep 17 00:00:00 2001 From: Brian Popow <38701097+brianpopow@users.noreply.github.com> Date: Sun, 20 Jan 2019 07:54:47 +0100 Subject: [PATCH 013/223] Added support for RLE4 encoded bitmaps (#812) --- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 132 +++++++++++++++++- .../Formats/Bmp/BmpDecoderTests.cs | 2 +- .../Formats/Bmp/BmpEncoderTests.cs | 2 +- tests/ImageSharp.Tests/TestImages.cs | 10 +- tests/Images/Input/Bmp/pal4rle.bmp | 3 + 5 files changed, 138 insertions(+), 11 deletions(-) create mode 100644 tests/Images/Input/Bmp/pal4rle.bmp diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 362fe64430..8ca698b878 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -157,7 +157,8 @@ namespace SixLabors.ImageSharp.Formats.Bmp break; case BmpCompression.RLE8: - this.ReadRle8(pixels, palette, this.infoHeader.Width, this.infoHeader.Height, inverted); + case BmpCompression.RLE4: + this.ReadRle(this.infoHeader.Compression, pixels, palette, this.infoHeader.Width, this.infoHeader.Height, inverted); break; @@ -254,22 +255,31 @@ namespace SixLabors.ImageSharp.Formats.Bmp } /// - /// Looks up color values and builds the image from de-compressed RLE8 data. + /// Looks up color values and builds the image from de-compressed RLE8 or RLE4 data. /// Compressed RLE8 stream is uncompressed by + /// Compressed RLE4 stream is uncompressed by /// /// The pixel format. + /// The compression type. Either RLE4 or RLE8. /// The to assign the palette to. /// The containing the colors. /// The width of the bitmap. /// The height of the bitmap. /// Whether the bitmap is inverted. - private void ReadRle8(Buffer2D pixels, byte[] colors, int width, int height, bool inverted) + private void ReadRle(BmpCompression compression, Buffer2D pixels, byte[] colors, int width, int height, bool inverted) where TPixel : struct, IPixel { TPixel color = default; using (Buffer2D buffer = this.memoryAllocator.Allocate2D(width, height, AllocationOptions.Clean)) { - this.UncompressRle8(width, buffer.GetSpan()); + if (compression == BmpCompression.RLE8) + { + this.UncompressRle8(width, buffer.GetSpan()); + } + else + { + this.UncompressRle4(width, buffer.GetSpan()); + } for (int y = 0; y < height; y++) { @@ -287,12 +297,122 @@ namespace SixLabors.ImageSharp.Formats.Bmp } /// - /// Produce uncompressed bitmap data from RLE8 stream. + /// Produce uncompressed bitmap data from a RLE4 stream. + /// + /// + /// RLE4 is a 2-byte run-length encoding. + ///
If first byte is 0, the second byte may have special meaning. + ///
Otherwise, the first byte is the length of the run and second byte contains two color indexes. + ///
+ /// The width of the bitmap. + /// Buffer for uncompressed data. + private void UncompressRle4(int w, Span buffer) + { +#if NETCOREAPP2_1 + Span cmd = stackalloc byte[2]; +#else + byte[] cmd = new byte[2]; +#endif + int count = 0; + + while (count < buffer.Length) + { + if (this.stream.Read(cmd, 0, cmd.Length) != 2) + { + throw new Exception("Failed to read 2 bytes from the stream"); + } + + if (cmd[0] == RleCommand) + { + switch (cmd[1]) + { + case RleEndOfBitmap: + return; + + case RleEndOfLine: + int extra = count % w; + if (extra > 0) + { + count += w - extra; + } + + break; + + case RleDelta: + int dx = this.stream.ReadByte(); + int dy = this.stream.ReadByte(); + count += (w * dy) + dx; + + break; + + default: + // If the second byte > 2, we are in 'absolute mode'. + // The second byte contains the number of color indexes that follow. + int max = cmd[1]; + int bytesToRead = (max + 1) / 2; + + byte[] run = new byte[bytesToRead]; + + this.stream.Read(run, 0, run.Length); + + int idx = 0; + for (int i = 0; i < max; i++) + { + byte twoPixels = run[idx]; + if (i % 2 == 0) + { + byte leftPixel = (byte)((twoPixels >> 4) & 0xF); + buffer[count++] = leftPixel; + } + else + { + byte rightPixel = (byte)(twoPixels & 0xF); + buffer[count++] = rightPixel; + idx++; + } + } + + // Absolute mode data is aligned to two-byte word-boundary + int padding = bytesToRead & 1; + + this.stream.Skip(padding); + + break; + } + } + else + { + int max = cmd[0]; + + // The second byte contains two color indexes, one in its high-order 4 bits and one in its low-order 4 bits. + byte twoPixels = cmd[1]; + byte rightPixel = (byte)(twoPixels & 0xF); + byte leftPixel = (byte)((twoPixels >> 4) & 0xF); + + for (int idx = 0; idx < max; idx++) + { + if (idx % 2 == 0) + { + buffer[count] = leftPixel; + } + else + { + buffer[count] = rightPixel; + } + + count++; + } + } + } + } + + /// + /// Produce uncompressed bitmap data from a RLE8 stream. /// /// /// RLE8 is a 2-byte run-length encoding. ///
If first byte is 0, the second byte may have special meaning. - ///
Otherwise, first byte is the length of the run and second byte is the color for the run. + ///
Otherwise, the first byte is the length of the run and second byte is the color for the run. ///
/// The width of the bitmap. /// Buffer for uncompressed data. diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index 60e45ae35f..0ebfbf311a 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Tests { { TestImages.Bmp.Car, 3780, 3780 , PixelResolutionUnit.PixelsPerMeter }, { TestImages.Bmp.V5Header, 3780, 3780 , PixelResolutionUnit.PixelsPerMeter }, - { TestImages.Bmp.RLE, 2835, 2835, PixelResolutionUnit.PixelsPerMeter } + { TestImages.Bmp.RLE8, 2835, 2835, PixelResolutionUnit.PixelsPerMeter } }; [Theory] diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs index b9f855cf12..f818be8a91 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Tests { { TestImages.Bmp.Car, 3780, 3780 , PixelResolutionUnit.PixelsPerMeter }, { TestImages.Bmp.V5Header, 3780, 3780 , PixelResolutionUnit.PixelsPerMeter }, - { TestImages.Bmp.RLE, 2835, 2835, PixelResolutionUnit.PixelsPerMeter } + { TestImages.Bmp.RLE8, 2835, 2835, PixelResolutionUnit.PixelsPerMeter } }; public static readonly TheoryData BmpBitsPerPixelFiles = diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 5ecc266572..6e6c7ce47a 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -195,7 +195,8 @@ namespace SixLabors.ImageSharp.Tests public const string NegHeight = "Bmp/neg_height.bmp"; public const string CoreHeader = "Bmp/BitmapCoreHeaderQR.bmp"; public const string V5Header = "Bmp/BITMAPV5HEADER.bmp"; - public const string RLE = "Bmp/RunLengthEncoded.bmp"; + public const string RLE8 = "Bmp/RunLengthEncoded.bmp"; + public const string RLE4 = "Bmp/pal4rle.bmp"; public const string RLEInverted = "Bmp/RunLengthEncoded-inverted.bmp"; public const string Bit1 = "Bmp/pal1.bmp"; public const string Bit1Pal1 = "Bmp/pal1p1.bmp"; @@ -209,6 +210,7 @@ namespace SixLabors.ImageSharp.Tests // Note: This format can be called OS/2 BMPv1, or Windows BMPv2 public const string WinBmpv2 = "Bmp/pal8os2v1_winv2.bmp"; + public const string WinBmpv3 = "Bmp/rgb24.bmp"; public const string WinBmpv4 = "Bmp/pal8v4.bmp"; public const string WinBmpv5 = "Bmp/pal8v5.bmp"; @@ -218,8 +220,8 @@ namespace SixLabors.ImageSharp.Tests // Bitmap images with compression type BITFIELDS public const string Rgb32bfdef = "Bmp/rgb32bfdef.bmp"; public const string Rgb32bf = "Bmp/rgb32bf.bmp"; - public const string Rgb16565 = "Bmp/rgb16-565.bmp"; public const string Rgb16bfdef = "Bmp/rgb16bfdef.bmp"; + public const string Rgb16565 = "Bmp/rgb16-565.bmp"; public const string Rgb16565pal = "Bmp/rgb16-565pal.bmp"; public const string Issue735 = "Bmp/issue735.bmp"; public const string Rgba32bf56 = "Bmp/rgba32h56.bmp"; @@ -241,7 +243,9 @@ namespace SixLabors.ImageSharp.Tests F, NegHeight, CoreHeader, - V5Header, RLE, + V5Header, + RLE4, + RLE8, RLEInverted, Bit1, Bit1Pal1, diff --git a/tests/Images/Input/Bmp/pal4rle.bmp b/tests/Images/Input/Bmp/pal4rle.bmp new file mode 100644 index 0000000000..b83a2a98d6 --- /dev/null +++ b/tests/Images/Input/Bmp/pal4rle.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3c26b6bdd22311e03b1ac0713b0a3b6af5e579bef16694cbea74ae0c4a2599e4 +size 3836 From 2c621120175e5503f51aabe544f0ec79980302fc Mon Sep 17 00:00:00 2001 From: Brian Popow <38701097+brianpopow@users.noreply.github.com> Date: Wed, 23 Jan 2019 13:22:58 +0100 Subject: [PATCH 014/223] Adds support for OS/2 version 2 bitmaps (#813) * Added support for OS/2 version 2 bitmaps * throw NotSupportedException, if the file header type is not BM * renamed Os2v2 to Os2v2Size * Added BmpThrowHelper similar to the JpegThrowHelper --- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 41 +++++++++----- src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs | 54 ++++++++++++++++++- src/ImageSharp/Formats/Bmp/BmpThrowHelper.cs | 28 ++++++++++ .../Formats/Bmp/BmpDecoderTests.cs | 18 ++++++- tests/ImageSharp.Tests/TestImages.cs | 1 + tests/Images/Input/Bmp/pal8os2v2.bmp | 3 ++ 6 files changed, 129 insertions(+), 16 deletions(-) create mode 100644 src/ImageSharp/Formats/Bmp/BmpThrowHelper.cs create mode 100644 tests/Images/Input/Bmp/pal8os2v2.bmp diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 8ca698b878..6bfdfa3a29 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -168,7 +168,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp break; default: - throw new NotSupportedException("Does not support this kind of bitmap files."); + BmpThrowHelper.ThrowNotSupportedException("Does not support this kind of bitmap files."); + + break; } return image; @@ -319,7 +321,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp { if (this.stream.Read(cmd, 0, cmd.Length) != 2) { - throw new Exception("Failed to read 2 bytes from the stream"); + BmpThrowHelper.ThrowImageFormatException("Failed to read 2 bytes from the stream while uncompressing RLE4 bitmap."); } if (cmd[0] == RleCommand) @@ -429,7 +431,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp { if (this.stream.Read(cmd, 0, cmd.Length) != 2) { - throw new Exception("Failed to read 2 bytes from stream"); + BmpThrowHelper.ThrowImageFormatException("Failed to read 2 bytes from stream while uncompressing RLE8 bitmap."); } if (cmd[0] == RleCommand) @@ -913,7 +915,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp int headerSize = BinaryPrimitives.ReadInt32LittleEndian(buffer); if (headerSize < BmpInfoHeader.CoreSize) { - throw new NotSupportedException($"ImageSharp does not support this BMP file. HeaderSize: {headerSize}."); + BmpThrowHelper.ThrowNotSupportedException($"ImageSharp does not support this BMP file. HeaderSize is '{headerSize}'."); } int skipAmount = 0; @@ -926,23 +928,23 @@ namespace SixLabors.ImageSharp.Formats.Bmp // read the rest of the header this.stream.Read(buffer, BmpInfoHeader.HeaderSizeSize, headerSize - BmpInfoHeader.HeaderSizeSize); - BmpInfoHeaderType inofHeaderType = BmpInfoHeaderType.WinVersion2; + BmpInfoHeaderType infoHeaderType = BmpInfoHeaderType.WinVersion2; if (headerSize == BmpInfoHeader.CoreSize) { // 12 bytes - inofHeaderType = BmpInfoHeaderType.WinVersion2; + infoHeaderType = BmpInfoHeaderType.WinVersion2; this.infoHeader = BmpInfoHeader.ParseCore(buffer); } else if (headerSize == BmpInfoHeader.Os22ShortSize) { // 16 bytes - inofHeaderType = BmpInfoHeaderType.Os2Version2Short; + infoHeaderType = BmpInfoHeaderType.Os2Version2Short; this.infoHeader = BmpInfoHeader.ParseOs22Short(buffer); } else if (headerSize == BmpInfoHeader.SizeV3) { // == 40 bytes - inofHeaderType = BmpInfoHeaderType.WinVersion3; + infoHeaderType = BmpInfoHeaderType.WinVersion3; this.infoHeader = BmpInfoHeader.ParseV3(buffer); // if the info header is BMP version 3 and the compression type is BITFIELDS, @@ -960,24 +962,30 @@ namespace SixLabors.ImageSharp.Formats.Bmp else if (headerSize == BmpInfoHeader.AdobeV3Size) { // == 52 bytes - inofHeaderType = BmpInfoHeaderType.AdobeVersion3; + infoHeaderType = BmpInfoHeaderType.AdobeVersion3; this.infoHeader = BmpInfoHeader.ParseAdobeV3(buffer, withAlpha: false); } else if (headerSize == BmpInfoHeader.AdobeV3WithAlphaSize) { // == 56 bytes - inofHeaderType = BmpInfoHeaderType.AdobeVersion3WithAlpha; + infoHeaderType = BmpInfoHeaderType.AdobeVersion3WithAlpha; this.infoHeader = BmpInfoHeader.ParseAdobeV3(buffer, withAlpha: true); } + else if (headerSize == BmpInfoHeader.Os2v2Size) + { + // == 64 bytes + infoHeaderType = BmpInfoHeaderType.Os2Version2; + this.infoHeader = BmpInfoHeader.ParseOs2Version2(buffer); + } else if (headerSize >= BmpInfoHeader.SizeV4) { // >= 108 bytes - inofHeaderType = headerSize == BmpInfoHeader.SizeV4 ? BmpInfoHeaderType.WinVersion4 : BmpInfoHeaderType.WinVersion5; + infoHeaderType = headerSize == BmpInfoHeader.SizeV4 ? BmpInfoHeaderType.WinVersion4 : BmpInfoHeaderType.WinVersion5; this.infoHeader = BmpInfoHeader.ParseV4(buffer); } else { - throw new NotSupportedException($"ImageSharp does not support this BMP file. HeaderSize: {headerSize}."); + BmpThrowHelper.ThrowNotSupportedException($"ImageSharp does not support this BMP file. HeaderSize '{headerSize}'."); } // Resolution is stored in PPM. @@ -1001,7 +1009,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp short bitsPerPixel = this.infoHeader.BitsPerPixel; this.bmpMetaData = this.metaData.GetFormatMetaData(BmpFormat.Instance); - this.bmpMetaData.InfoHeaderType = inofHeaderType; + this.bmpMetaData.InfoHeaderType = infoHeaderType; // We can only encode at these bit rates so far. if (bitsPerPixel.Equals((short)BmpBitsPerPixel.Pixel24) @@ -1027,6 +1035,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp this.stream.Read(buffer, 0, BmpFileHeader.Size); this.fileHeader = BmpFileHeader.Parse(buffer); + + if (this.fileHeader.Type != BmpConstants.TypeMarkers.Bitmap) + { + BmpThrowHelper.ThrowNotSupportedException($"ImageSharp does not support this BMP file. File header bitmap type marker '{this.fileHeader.Type}'."); + } } /// @@ -1080,7 +1093,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp // 256 * 4 if (colorMapSize > 1024) { - throw new ImageFormatException($"Invalid bmp colormap size '{colorMapSize}'"); + BmpThrowHelper.ThrowImageFormatException($"Invalid bmp colormap size '{colorMapSize}'"); } palette = new byte[colorMapSize]; diff --git a/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs b/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs index 316df4acc9..6da5f73e3f 100644 --- a/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs +++ b/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs @@ -41,6 +41,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// public const int AdobeV3WithAlphaSize = 56; + /// + /// Size of a IBM OS/2 2.x bitmap header. + /// + public const int Os2v2Size = 64; + /// /// Defines the size of the BITMAPINFOHEADER (BMP Version 4) data structure in the bitmap file. /// @@ -117,7 +122,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp } /// - /// Gets or sets the size of this header + /// Gets or sets the size of this header. /// public int HeaderSize { get; set; } @@ -346,6 +351,53 @@ namespace SixLabors.ImageSharp.Formats.Bmp alphaMask: withAlpha ? BinaryPrimitives.ReadInt32LittleEndian(data.Slice(52, 4)) : 0); } + /// + /// Parses a OS/2 version 2 bitmap header (64 bytes). Only the first 40 bytes are parsed which are + /// very similar to the Bitmap v3 header. The other 24 bytes are ignored, but they do not hold any + /// useful information for decoding the image. + /// + /// The data to parse. + /// The parsed header. + /// + public static BmpInfoHeader ParseOs2Version2(ReadOnlySpan data) + { + var infoHeader = new BmpInfoHeader( + headerSize: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4)), + width: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)), + height: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)), + planes: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(12, 2)), + bitsPerPixel: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(14, 2))); + + int compression = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(16, 4)); + + // The compression value in OS/2 bitmap has a different meaning than in windows bitmaps. + // Map the OS/2 value to the windows values. + switch (compression) + { + case 0: + infoHeader.Compression = BmpCompression.RGB; + break; + case 1: + infoHeader.Compression = BmpCompression.RLE8; + break; + case 2: + infoHeader.Compression = BmpCompression.RLE4; + break; + default: + BmpThrowHelper.ThrowImageFormatException($"Compression type is not supported. ImageSharp only supports uncompressed, RLE4 and RLE8."); + break; + } + + infoHeader.ImageSize = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(20, 4)); + infoHeader.XPelsPerMeter = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(24, 4)); + infoHeader.YPelsPerMeter = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(28, 4)); + infoHeader.ClrUsed = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(32, 4)); + infoHeader.ClrImportant = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(36, 4)); + + // The following 24 bytes of the header are omitted. + return infoHeader; + } + /// /// Parses the full BMP Version 4 BITMAPINFOHEADER header (108 bytes). /// diff --git a/src/ImageSharp/Formats/Bmp/BmpThrowHelper.cs b/src/ImageSharp/Formats/Bmp/BmpThrowHelper.cs new file mode 100644 index 0000000000..dae044ddbd --- /dev/null +++ b/src/ImageSharp/Formats/Bmp/BmpThrowHelper.cs @@ -0,0 +1,28 @@ +using System; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.Formats.Bmp +{ + internal static class BmpThrowHelper + { + /// + /// Cold path optimization for throwing -s + /// + /// The error message for the exception. + [MethodImpl(MethodImplOptions.NoInlining)] + public static void ThrowImageFormatException(string errorMessage) + { + throw new ImageFormatException(errorMessage); + } + + /// + /// Cold path optimization for throwing -s + /// + /// The error message for the exception. + [MethodImpl(MethodImplOptions.NoInlining)] + public static void ThrowNotSupportedException(string errorMessage) + { + throw new NotSupportedException(errorMessage); + } + } +} diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index 0ebfbf311a..5d7d35dd58 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -214,12 +214,28 @@ namespace SixLabors.ImageSharp.Tests { using (Image image = provider.GetImage(new BmpDecoder())) { - image.DebugSave(provider, "png"); + image.DebugSave(provider); // TODO: Neither System.Drawing not MagickReferenceDecoder // can correctly decode this file. // image.CompareToOriginal(provider); } } + + [Theory] + [WithFile(Os2v2, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecode_Os2v2Header(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder())) + { + image.DebugSave(provider); + + // TODO: System.Drawing can not decode this image. MagickReferenceDecoder can decode it, + // but i think incorrectly. I have loaded the image with GIMP and exported as PNG. + // The results are the same as the image sharp implementation. + // image.CompareToOriginal(provider, new MagickReferenceDecoder()); + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 6e6c7ce47a..d83fe4907d 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -216,6 +216,7 @@ namespace SixLabors.ImageSharp.Tests public const string WinBmpv5 = "Bmp/pal8v5.bmp"; public const string Bit8Palette4 = "Bmp/pal8-0.bmp"; public const string Os2v2Short = "Bmp/pal8os2v2-16.bmp"; + public const string Os2v2 = "Bmp/pal8os2v2.bmp"; // Bitmap images with compression type BITFIELDS public const string Rgb32bfdef = "Bmp/rgb32bfdef.bmp"; diff --git a/tests/Images/Input/Bmp/pal8os2v2.bmp b/tests/Images/Input/Bmp/pal8os2v2.bmp new file mode 100644 index 0000000000..11473a632b --- /dev/null +++ b/tests/Images/Input/Bmp/pal8os2v2.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e2597b53091d734900068b42cddaed7430261cc5eade32b4f19fd6c2c4568a52 +size 9278 From 2bf060f40b8a7f49554130599e07990bea832e1d Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 25 Jan 2019 16:32:45 +1100 Subject: [PATCH 015/223] Update dependencies + fix stylecop (#816) --- .gitmodules | 3 + ImageSharp.sln | 5 +- .../ImageSharp.Drawing.csproj | 97 ++++++++++--------- .../Helpers/SimdUtils.ExtendedIntrinsics.cs | 5 +- src/ImageSharp/Common/Helpers/TestHelpers.cs | 2 +- .../ParallelExecutionSettings.cs | 2 +- .../Common/ParallelUtils/ParallelHelper.cs | 2 +- src/ImageSharp/Common/Tuples/Octet.cs | 5 +- src/ImageSharp/Common/Tuples/Vector4Pair.cs | 5 +- .../Formats/Bmp/BmpInfoHeaderType.cs | 5 +- src/ImageSharp/Formats/Bmp/BmpThrowHelper.cs | 5 +- .../Formats/Gif/Sections/IGifExtension.cs | 5 +- .../Jpeg/Components/Decoder/ScanDecoder.cs | 3 +- .../Encoder/YCbCrForwardConverter{TPixel}.cs | 5 +- .../Formats/Jpeg/JpegEncoderCore.cs | 37 ++++++- .../Formats/Png/Chunks/PhysicalChunkData.cs | 5 +- .../Formats/Png/Filters/UpFilter.cs | 3 +- src/ImageSharp/Formats/Png/PngConstants.cs | 3 +- src/ImageSharp/ImageSharp.csproj | 21 ++-- src/ImageSharp/Memory/RowInterval.cs | 2 +- .../MetaData/Profiles/Exif/ExifConstants.cs | 6 +- .../MetaData/Profiles/Exif/ExifTags.cs | 2 +- .../TagDataEntries/IccLut16TagDataEntry.cs | 7 +- .../ICC/TagDataEntries/IccLut8TagDataEntry.cs | 7 +- .../Utils/Vector4Converters.Default.cs | 5 +- .../Utils/Vector4Converters.RgbaCompatible.cs | 5 +- standards | 1 + 27 files changed, 167 insertions(+), 86 deletions(-) create mode 160000 standards diff --git a/.gitmodules b/.gitmodules index e7972649f4..37ef701cdf 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,3 +2,6 @@ path = tests/Images/External url = https://github.com/SixLabors/Imagesharp.Tests.Images.git branch = master +[submodule "standards"] + path = standards + url = https://github.com/SixLabors/Standards diff --git a/ImageSharp.sln b/ImageSharp.sln index 8f3bc68602..3c3817bf63 100644 --- a/ImageSharp.sln +++ b/ImageSharp.sln @@ -5,7 +5,7 @@ VisualStudioVersion = 15.0.26730.12 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{C317F1B1-D75E-4C6D-83EB-80367343E0D7}" ProjectSection(SolutionItems) = preProject - .editorconfig = .editorconfig + standards\.editorconfig = standards\.editorconfig .travis.yml = .travis.yml appveyor.yml = appveyor.yml .github\ISSUE_TEMPLATE\ask-question.md = .github\ISSUE_TEMPLATE\ask-question.md @@ -15,12 +15,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionIt .github\CONTRIBUTING.md = .github\CONTRIBUTING.md .github\ISSUE_TEMPLATE\feature-request.md = .github\ISSUE_TEMPLATE\feature-request.md features.md = features.md - ImageSharp.ruleset = ImageSharp.ruleset ImageSharp.sln.DotSettings = ImageSharp.sln.DotSettings NuGet.config = NuGet.config .github\PULL_REQUEST_TEMPLATE.md = .github\PULL_REQUEST_TEMPLATE.md README.md = README.md run-tests.ps1 = run-tests.ps1 + standards\SixLabors.ruleset = standards\SixLabors.ruleset + standards\stylecop.json = standards\stylecop.json EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source", "Source", "{815C0625-CD3D-440F-9F80-2D83856AB7AE}" diff --git a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj index a6f529ee36..f4906d25e3 100644 --- a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj +++ b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj @@ -1,50 +1,53 @@  - - SixLabors.ImageSharp.Drawing - SixLabors and contributors - Six Labors - Copyright (c) Six Labors and contributors. - SixLabors.ImageSharp - An extension to ImageSharp that allows the drawing of images, paths, and text. - en + + SixLabors.ImageSharp.Drawing + SixLabors and contributors + Six Labors + Copyright (c) Six Labors and contributors. + SixLabors.ImageSharp + An extension to ImageSharp that allows the drawing of images, paths, and text. + en - $(packageversion) - 0.0.1 - netstandard1.3;netstandard2.0 - 7.3 - true - true - SixLabors.ImageSharp.Drawing - SixLabors.ImageSharp.Drawing - Image Draw Shape Path Font - https://raw.githubusercontent.com/SixLabors/Branding/master/icons/imagesharp/sixlabors.imagesharp.128.png - https://github.com/SixLabors/ImageSharp - http://www.apache.org/licenses/LICENSE-2.0 - git - https://github.com/SixLabors/ImageSharp - full - portable - True - - - - - - - - - - - - - All - - - - ..\..\ImageSharp.ruleset - SixLabors.ImageSharp - - - true - + $(packageversion) + 0.0.1 + netstandard1.3;netstandard2.0 + 7.3 + true + true + SixLabors.ImageSharp.Drawing + SixLabors.ImageSharp.Drawing + Image Draw Shape Path Font + https://raw.githubusercontent.com/SixLabors/Branding/master/icons/imagesharp/sixlabors.imagesharp.128.png + https://github.com/SixLabors/ImageSharp + http://www.apache.org/licenses/LICENSE-2.0 + git + https://github.com/SixLabors/ImageSharp + full + portable + True + + + + + + + + + + + + + + + + + + + ..\..\standards\SixLabors.ruleset + SixLabors.ImageSharp + + + + true + \ No newline at end of file diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs b/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs index 9aeb209319..83216aaa72 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/ImageSharp/Common/Helpers/TestHelpers.cs b/src/ImageSharp/Common/Helpers/TestHelpers.cs index fd16a577ab..d330233c4c 100644 --- a/src/ImageSharp/Common/Helpers/TestHelpers.cs +++ b/src/ImageSharp/Common/Helpers/TestHelpers.cs @@ -1,4 +1,4 @@ -// Copyright(c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Common.Helpers diff --git a/src/ImageSharp/Common/ParallelUtils/ParallelExecutionSettings.cs b/src/ImageSharp/Common/ParallelUtils/ParallelExecutionSettings.cs index 0b45719c38..40163bc789 100644 --- a/src/ImageSharp/Common/ParallelUtils/ParallelExecutionSettings.cs +++ b/src/ImageSharp/Common/ParallelUtils/ParallelExecutionSettings.cs @@ -1,4 +1,4 @@ -// Copyright(c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System.Threading.Tasks; diff --git a/src/ImageSharp/Common/ParallelUtils/ParallelHelper.cs b/src/ImageSharp/Common/ParallelUtils/ParallelHelper.cs index 782333219f..a930b8390f 100644 --- a/src/ImageSharp/Common/ParallelUtils/ParallelHelper.cs +++ b/src/ImageSharp/Common/ParallelUtils/ParallelHelper.cs @@ -1,4 +1,4 @@ -// Copyright(c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; diff --git a/src/ImageSharp/Common/Tuples/Octet.cs b/src/ImageSharp/Common/Tuples/Octet.cs index 539b74e324..23faf08ae9 100644 --- a/src/ImageSharp/Common/Tuples/Octet.cs +++ b/src/ImageSharp/Common/Tuples/Octet.cs @@ -1,4 +1,7 @@ -using System.Runtime.CompilerServices; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.Tuples diff --git a/src/ImageSharp/Common/Tuples/Vector4Pair.cs b/src/ImageSharp/Common/Tuples/Vector4Pair.cs index 2e5ca31371..b3a32deeef 100644 --- a/src/ImageSharp/Common/Tuples/Vector4Pair.cs +++ b/src/ImageSharp/Common/Tuples/Vector4Pair.cs @@ -1,4 +1,7 @@ -using System.Numerics; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/ImageSharp/Formats/Bmp/BmpInfoHeaderType.cs b/src/ImageSharp/Formats/Bmp/BmpInfoHeaderType.cs index a4ce0115f9..a92a19d9ba 100644 --- a/src/ImageSharp/Formats/Bmp/BmpInfoHeaderType.cs +++ b/src/ImageSharp/Formats/Bmp/BmpInfoHeaderType.cs @@ -1,4 +1,7 @@ -namespace SixLabors.ImageSharp.Formats.Bmp +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats.Bmp { /// /// Enum value for the different bitmap info header types. The enum value is the number of bytes for the specific bitmap header. diff --git a/src/ImageSharp/Formats/Bmp/BmpThrowHelper.cs b/src/ImageSharp/Formats/Bmp/BmpThrowHelper.cs index dae044ddbd..443471404e 100644 --- a/src/ImageSharp/Formats/Bmp/BmpThrowHelper.cs +++ b/src/ImageSharp/Formats/Bmp/BmpThrowHelper.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; using System.Runtime.CompilerServices; namespace SixLabors.ImageSharp.Formats.Bmp diff --git a/src/ImageSharp/Formats/Gif/Sections/IGifExtension.cs b/src/ImageSharp/Formats/Gif/Sections/IGifExtension.cs index 2fdc233b0c..c8bd286746 100644 --- a/src/ImageSharp/Formats/Gif/Sections/IGifExtension.cs +++ b/src/ImageSharp/Formats/Gif/Sections/IGifExtension.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; namespace SixLabors.ImageSharp.Formats.Gif { diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs index 48abca2a4e..a1c1b023e4 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs @@ -474,7 +474,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder Unsafe.Add(ref blockDataRef, zig) = (short)this.ExtendReceive(s); } } - } while (k < 64); + } + while (k < 64); } private void DecodeBlockProgressiveDC( diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs index d775425c5c..301079b6ae 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Advanced; diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs index 0dcbd8fef7..b3f30f815f 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs @@ -547,7 +547,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg private void WriteDefineHuffmanTables(int componentCount) { // Table identifiers. - Span headers = stackalloc byte[] { 0x00, 0x10, 0x01, 0x11 }; + Span headers = stackalloc byte[] + { + 0x00, + 0x10, + 0x01, + 0x11 + }; int markerlen = 2; HuffmanSpec[] specs = HuffmanSpec.TheHuffmanSpecs; @@ -786,16 +792,37 @@ namespace SixLabors.ImageSharp.Formats.Jpeg private void WriteStartOfFrame(int width, int height, int componentCount) { // "default" to 4:2:0 - Span subsamples = stackalloc byte[] { 0x22, 0x11, 0x11 }; - Span chroma = stackalloc byte[] { 0x00, 0x01, 0x01 }; + Span subsamples = stackalloc byte[] + { + 0x22, + 0x11, + 0x11 + }; + + Span chroma = stackalloc byte[] + { + 0x00, + 0x01, + 0x01 + }; switch (this.subsample) { case JpegSubsample.Ratio444: - subsamples = stackalloc byte[] { 0x11, 0x11, 0x11 }; + subsamples = stackalloc byte[] + { + 0x11, + 0x11, + 0x11 + }; break; case JpegSubsample.Ratio420: - subsamples = stackalloc byte[] { 0x22, 0x11, 0x11 }; + subsamples = stackalloc byte[] + { + 0x22, + 0x11, + 0x11 + }; break; } diff --git a/src/ImageSharp/Formats/Png/Chunks/PhysicalChunkData.cs b/src/ImageSharp/Formats/Png/Chunks/PhysicalChunkData.cs index 6ab0dd6576..1d0e280bd3 100644 --- a/src/ImageSharp/Formats/Png/Chunks/PhysicalChunkData.cs +++ b/src/ImageSharp/Formats/Png/Chunks/PhysicalChunkData.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; using System.Buffers.Binary; using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.MetaData; diff --git a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs index c6a297e33a..5d9dc6a890 100644 --- a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs @@ -57,7 +57,8 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters // Up(x) = Raw(x) - Prior(x) resultBaseRef = 2; - for (int x = 0; x < scanline.Length; /* Note: ++x happens in the body to avoid one add operation */) { + for (int x = 0; x < scanline.Length; /* Note: ++x happens in the body to avoid one add operation */) + { byte scan = Unsafe.Add(ref scanBaseRef, x); byte above = Unsafe.Add(ref prevBaseRef, x); ++x; diff --git a/src/ImageSharp/Formats/Png/PngConstants.cs b/src/ImageSharp/Formats/Png/PngConstants.cs index 62a7b74aba..e1f978e1ac 100644 --- a/src/ImageSharp/Formats/Png/PngConstants.cs +++ b/src/ImageSharp/Formats/Png/PngConstants.cs @@ -26,7 +26,8 @@ namespace SixLabors.ImageSharp.Formats.Png /// public static readonly IEnumerable FileExtensions = new[] { "png" }; - public static readonly byte[] HeaderBytes = { + public static readonly byte[] HeaderBytes = + { 0x89, // Set the high bit. 0x50, // P 0x4E, // N diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index 1cdee81a26..6ab609d5c0 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -31,20 +31,17 @@ $(DefineConstants);SUPPORTS_EXTENDED_INTRINSICS - + - + + + + - - - - All - - @@ -52,14 +49,16 @@ - + - ..\..\ImageSharp.ruleset + ..\..\standards\SixLabors.ruleset SixLabors.ImageSharp + true + TextTemplatingFileGenerator @@ -122,6 +121,7 @@ TextTemplatingFileGenerator + True @@ -199,6 +199,7 @@ PorterDuffFunctions.Generated.tt + diff --git a/src/ImageSharp/Memory/RowInterval.cs b/src/ImageSharp/Memory/RowInterval.cs index 0750e0368c..501ed5e69c 100644 --- a/src/ImageSharp/Memory/RowInterval.cs +++ b/src/ImageSharp/Memory/RowInterval.cs @@ -1,4 +1,4 @@ -// Copyright(c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using SixLabors.Primitives; diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifConstants.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifConstants.cs index 555cadafee..fb2a893d1c 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifConstants.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifConstants.cs @@ -5,14 +5,16 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif { internal static class ExifConstants { - public static readonly byte[] LittleEndianByteOrderMarker = { + public static readonly byte[] LittleEndianByteOrderMarker = + { (byte)'I', (byte)'I', 0x2A, 0x00, }; - public static readonly byte[] BigEndianByteOrderMarker = { + public static readonly byte[] BigEndianByteOrderMarker = + { (byte)'M', (byte)'M', 0x00, diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifTags.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifTags.cs index e497fc7fa4..e999d00b86 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifTags.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifTags.cs @@ -238,7 +238,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif LensMake, LensModel, LensSerialNumber - }; + }; /// /// The collection of GPS tags diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut16TagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut16TagDataEntry.cs index dd180b2997..415f6941ec 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut16TagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut16TagDataEntry.cs @@ -13,7 +13,12 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc /// internal sealed class IccLut16TagDataEntry : IccTagDataEntry, IEquatable { - private static readonly float[,] IdentityMatrix = { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } }; + private static readonly float[,] IdentityMatrix = + { + { 1, 0, 0 }, + { 0, 1, 0 }, + { 0, 0, 1 } + }; /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut8TagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut8TagDataEntry.cs index b345d14a54..04a49e316d 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut8TagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut8TagDataEntry.cs @@ -13,7 +13,12 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc /// internal sealed class IccLut8TagDataEntry : IccTagDataEntry, IEquatable { - private static readonly float[,] IdentityMatrix = { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } }; + private static readonly float[,] IdentityMatrix = + { + { 1, 0, 0 }, + { 0, 1, 0 }, + { 0, 0, 1 } + }; /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs index 139dbfa10f..e784e3b5a6 100644 --- a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs +++ b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs index 5609e606d8..06c33d79c7 100644 --- a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs +++ b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; using System.Buffers; using System.Numerics; using System.Runtime.CompilerServices; diff --git a/standards b/standards new file mode 160000 index 0000000000..dd83f64963 --- /dev/null +++ b/standards @@ -0,0 +1 @@ +Subproject commit dd83f649638c6333984a757c01be6ec294e6b63c From d062ffa064cdf1c7ef30d4188d13c0b9065a5c22 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 29 Jan 2019 10:16:31 +1100 Subject: [PATCH 016/223] Track input stream position on load. (#820) --- src/ImageSharp/IO/DoubleBufferedStreamReader.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ImageSharp/IO/DoubleBufferedStreamReader.cs b/src/ImageSharp/IO/DoubleBufferedStreamReader.cs index 94a2f2cbfc..8530ecec52 100644 --- a/src/ImageSharp/IO/DoubleBufferedStreamReader.cs +++ b/src/ImageSharp/IO/DoubleBufferedStreamReader.cs @@ -42,6 +42,7 @@ namespace SixLabors.ImageSharp.IO public DoubleBufferedStreamReader(MemoryAllocator memoryAllocator, Stream stream) { this.stream = stream; + this.Position = (int)stream.Position; this.length = (int)stream.Length; this.managedBuffer = memoryAllocator.AllocateManagedByteBuffer(ChunkLength, AllocationOptions.Clean); this.bufferChunk = this.managedBuffer.Array; From fb9e23020bf0882956ef9c509ba9dccbb09cd2f9 Mon Sep 17 00:00:00 2001 From: Brian Popow <38701097+brianpopow@users.noreply.github.com> Date: Tue, 5 Feb 2019 02:41:26 +0100 Subject: [PATCH 017/223] Fix Decoding 8-Bit grayscale png's with alpha (#830) --- .../Formats/Png/PngScanlineProcessor.cs | 3 +- .../Formats/Png/PngDecoderTests.cs | 30 +++++++++++++++++++ tests/ImageSharp.Tests/TestImages.cs | 1 + tests/Images/Input/Png/rollsroyce.png | 3 ++ 4 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 tests/Images/Input/Png/rollsroyce.png diff --git a/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs b/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs index 4242b2d554..40c2841176 100644 --- a/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs +++ b/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs @@ -240,9 +240,9 @@ namespace SixLabors.ImageSharp.Formats.Png else { Rgba32 rgba32 = default; + int offset = 0; for (int x = pixelOffset; x < header.Width; x += increment) { - int offset = x * bytesPerPixel; byte luminance = Unsafe.Add(ref scanlineSpanRef, offset); byte alpha = Unsafe.Add(ref scanlineSpanRef, offset + bytesPerSample); rgba32.R = luminance; @@ -252,6 +252,7 @@ namespace SixLabors.ImageSharp.Formats.Png pixel.FromRgba32(rgba32); Unsafe.Add(ref rowSpanRef, x) = pixel; + offset += bytesPerPixel; } } } diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index f51f9b6c5c..1452f233b6 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -67,6 +67,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png TestImages.Png.GrayTrns16BitInterlaced }; + public static readonly string[] TestImagesGrayAlpha8Bit = + { + TestImages.Png.GrayAlpha8Bit, + TestImages.Png.GrayAlpha8Bit2 + }; + public static readonly TheoryData RatioFiles = new TheoryData { @@ -123,6 +129,18 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png } } + [Theory] + [WithFileCollection(nameof(TestImagesGrayAlpha8Bit), PixelTypes.Rgba32)] + public void Decoder_Gray8bitWithAlpha(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new PngDecoder())) + { + image.DebugSave(provider); + image.CompareToOriginal(provider, ImageComparer.Exact); + } + } + [Theory] [WithFileCollection(nameof(TestImagesGray16Bit), PixelTypes.Rgb48)] public void Decode_Gray16Bit(TestImageProvider provider) @@ -159,6 +177,18 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png } } + [Theory] + [WithFile(TestImages.Png.GrayAlpha8Bit2, PixelTypes)] + public void Decoder_CanDecodeGrey8bitWithAlpha(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new PngDecoder())) + { + image.DebugSave(provider); + image.CompareToOriginal(provider, ImageComparer.Exact); + } + } + [Fact] public void Decode_IgnoreMetadataIsFalse_TextChunckIsRead() { diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index d83fe4907d..92773df7e4 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -29,6 +29,7 @@ namespace SixLabors.ImageSharp.Tests public const string Gray4Bpp = "Png/gray_4bpp.png"; public const string Gray16Bit = "Png/gray-16.png"; public const string GrayAlpha8Bit = "Png/gray-alpha-8.png"; + public const string GrayAlpha8Bit2 = "Png/rollsroyce.png"; public const string GrayAlpha16Bit = "Png/gray-alpha-16.png"; public const string GrayTrns16BitInterlaced = "Png/gray-16-tRNS-interlaced.png"; public const string Rgb24BppTrans = "Png/rgb-8-tRNS.png"; diff --git a/tests/Images/Input/Png/rollsroyce.png b/tests/Images/Input/Png/rollsroyce.png new file mode 100644 index 0000000000..c2b9199ac6 --- /dev/null +++ b/tests/Images/Input/Png/rollsroyce.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:90017d23999f41be7ac525648d8f435dcf8d1481ab98ebfd01a9ce87241e534f +size 25400 From 07aa4f9316c5e487d83df9ef0f12e2241d27c5ef Mon Sep 17 00:00:00 2001 From: Brian Popow <38701097+brianpopow@users.noreply.github.com> Date: Tue, 5 Feb 2019 05:33:16 +0100 Subject: [PATCH 018/223] Respecting fileHeader Offset, skipping bytes if necessary (#819) --- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 11 +++++++++++ .../ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs | 12 ++++++++++++ tests/ImageSharp.Tests/TestImages.cs | 1 + tests/Images/Input/Bmp/pal8offs.bmp | 3 +++ 4 files changed, 27 insertions(+) create mode 100644 tests/Images/Input/Bmp/pal8offs.bmp diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 6bfdfa3a29..496a6df4a2 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -1103,6 +1103,17 @@ namespace SixLabors.ImageSharp.Formats.Bmp this.infoHeader.VerifyDimensions(); + int skipAmount = this.fileHeader.Offset - (int)this.stream.Position; + if ((skipAmount + (int)this.stream.Position) > this.stream.Length) + { + BmpThrowHelper.ThrowImageFormatException($"Invalid fileheader offset found. Offset is greater than the stream length."); + } + + if (skipAmount > 0) + { + this.stream.Skip(skipAmount); + } + return bytesPerColorMapEntry; } } diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index 5d7d35dd58..e7b5aeaeae 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -150,6 +150,18 @@ namespace SixLabors.ImageSharp.Tests } } + [Theory] + [WithFile(Pal8Offset, PixelTypes.Rgba32)] + public void BmpDecoder_RespectsFileHeaderOffset(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder())) + { + image.DebugSave(provider); + image.CompareToOriginal(provider); + } + } + [Theory] [WithFile(F, CommonNonDefaultPixelTypes)] public void BmpDecoder_IsNotBoundToSinglePixelType(TestImageProvider provider) diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 92773df7e4..b4b75bfd8a 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -218,6 +218,7 @@ namespace SixLabors.ImageSharp.Tests public const string Bit8Palette4 = "Bmp/pal8-0.bmp"; public const string Os2v2Short = "Bmp/pal8os2v2-16.bmp"; public const string Os2v2 = "Bmp/pal8os2v2.bmp"; + public const string Pal8Offset = "Bmp/pal8offs.bmp"; // Bitmap images with compression type BITFIELDS public const string Rgb32bfdef = "Bmp/rgb32bfdef.bmp"; diff --git a/tests/Images/Input/Bmp/pal8offs.bmp b/tests/Images/Input/Bmp/pal8offs.bmp new file mode 100644 index 0000000000..8734a5b1c3 --- /dev/null +++ b/tests/Images/Input/Bmp/pal8offs.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:15ad578b6a6253334616b9e7e3a958f827b885eeffac1e3cf7e5247f96aca342 +size 9354 From 66f77952f7cfac680d6c1805a46c6dc7f37d34d3 Mon Sep 17 00:00:00 2001 From: Brian Popow <38701097+brianpopow@users.noreply.github.com> Date: Thu, 7 Feb 2019 00:57:43 +0100 Subject: [PATCH 019/223] Fix Decoding interlaced grayscale (#831) --- .../Formats/Png/PngScanlineProcessor.cs | 4 ++-- .../Formats/Png/PngDecoderTests.cs | 20 ++++++++++--------- tests/ImageSharp.Tests/TestImages.cs | 5 ++++- tests/Images/Input/Png/iftbbn0g01.png | 3 +++ tests/Images/Input/Png/iftbbn0g02.png | 3 +++ tests/Images/Input/Png/iftbbn0g04.png | 3 +++ 6 files changed, 26 insertions(+), 12 deletions(-) create mode 100644 tests/Images/Input/Png/iftbbn0g01.png create mode 100644 tests/Images/Input/Png/iftbbn0g02.png create mode 100644 tests/Images/Input/Png/iftbbn0g04.png diff --git a/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs b/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs index 40c2841176..c23694951d 100644 --- a/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs +++ b/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs @@ -145,9 +145,9 @@ namespace SixLabors.ImageSharp.Formats.Png { byte scaledLuminanceTrans = (byte)(luminanceTrans.PackedValue * scaleFactor); Rgba32 rgba32 = default; - for (int x = pixelOffset; x < header.Width; x += increment) + for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o++) { - byte luminance = (byte)(Unsafe.Add(ref scanlineSpanRef, x) * scaleFactor); + byte luminance = (byte)(Unsafe.Add(ref scanlineSpanRef, o) * scaleFactor); rgba32.R = luminance; rgba32.G = luminance; rgba32.B = luminance; diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index 1452f233b6..eca7a2543a 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -67,10 +67,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png TestImages.Png.GrayTrns16BitInterlaced }; - public static readonly string[] TestImagesGrayAlpha8Bit = + public static readonly string[] TestImagesGray8BitInterlaced = { - TestImages.Png.GrayAlpha8Bit, - TestImages.Png.GrayAlpha8Bit2 + TestImages.Png.GrayAlpha1BitInterlaced, + TestImages.Png.GrayAlpha2BitInterlaced, + TestImages.Png.Gray4BitInterlaced, + TestImages.Png.GrayAlpha8BitInterlaced }; public static readonly TheoryData RatioFiles = @@ -130,8 +132,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png } [Theory] - [WithFileCollection(nameof(TestImagesGrayAlpha8Bit), PixelTypes.Rgba32)] - public void Decoder_Gray8bitWithAlpha(TestImageProvider provider) + [WithFileCollection(nameof(TestImagesGray8BitInterlaced), PixelTypes.Rgba32)] + public void Decoder_Gray8bitInterlaced(TestImageProvider provider) where TPixel : struct, IPixel { using (Image image = provider.GetImage(new PngDecoder())) @@ -166,8 +168,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png } [Theory] - [WithFile(TestImages.Png.Splash, PixelTypes)] - public void Decoder_IsNotBoundToSinglePixelType(TestImageProvider provider) + [WithFile(TestImages.Png.GrayAlpha8BitInterlaced, PixelTypes)] + public void Decoder_CanDecodeGrey8bitWithAlpha(TestImageProvider provider) where TPixel : struct, IPixel { using (Image image = provider.GetImage(new PngDecoder())) @@ -178,8 +180,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png } [Theory] - [WithFile(TestImages.Png.GrayAlpha8Bit2, PixelTypes)] - public void Decoder_CanDecodeGrey8bitWithAlpha(TestImageProvider provider) + [WithFile(TestImages.Png.Splash, PixelTypes)] + public void Decoder_IsNotBoundToSinglePixelType(TestImageProvider provider) where TPixel : struct, IPixel { using (Image image = provider.GetImage(new PngDecoder())) diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index b4b75bfd8a..51e2d084c6 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -29,7 +29,10 @@ namespace SixLabors.ImageSharp.Tests public const string Gray4Bpp = "Png/gray_4bpp.png"; public const string Gray16Bit = "Png/gray-16.png"; public const string GrayAlpha8Bit = "Png/gray-alpha-8.png"; - public const string GrayAlpha8Bit2 = "Png/rollsroyce.png"; + public const string GrayAlpha8BitInterlaced = "Png/rollsroyce.png"; + public const string GrayAlpha1BitInterlaced = "Png/iftbbn0g01.png"; + public const string GrayAlpha2BitInterlaced = "Png/iftbbn0g02.png"; + public const string Gray4BitInterlaced = "Png/iftbbn0g04.png"; public const string GrayAlpha16Bit = "Png/gray-alpha-16.png"; public const string GrayTrns16BitInterlaced = "Png/gray-16-tRNS-interlaced.png"; public const string Rgb24BppTrans = "Png/rgb-8-tRNS.png"; diff --git a/tests/Images/Input/Png/iftbbn0g01.png b/tests/Images/Input/Png/iftbbn0g01.png new file mode 100644 index 0000000000..f11869beac --- /dev/null +++ b/tests/Images/Input/Png/iftbbn0g01.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1eea6f2448e9d3f4365b34d1f849c057c7199564f18cecad07c3a362926b11db +size 214 diff --git a/tests/Images/Input/Png/iftbbn0g02.png b/tests/Images/Input/Png/iftbbn0g02.png new file mode 100644 index 0000000000..a1e2e9f1e6 --- /dev/null +++ b/tests/Images/Input/Png/iftbbn0g02.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:82324d48b769628d4d8a4b6e7830c6d7dc778e5656d6e1e7556f4e53737a1dfc +size 211 diff --git a/tests/Images/Input/Png/iftbbn0g04.png b/tests/Images/Input/Png/iftbbn0g04.png new file mode 100644 index 0000000000..64b662e352 --- /dev/null +++ b/tests/Images/Input/Png/iftbbn0g04.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:47d393150de4cec7152f1ed61ea25bca9de628865e37ad735e7f162c58340916 +size 489 From a2c4b2d5d81a5ee8368431e2d5578715cc645bb7 Mon Sep 17 00:00:00 2001 From: Brian Popow <38701097+brianpopow@users.noreply.github.com> Date: Mon, 11 Feb 2019 05:33:03 +0100 Subject: [PATCH 020/223] Add support for Decoding BI_ALPHABITFIELDS (#832) * Adds support for BI_ALPHABITFIELDS * Fix for decoding bitmaps with a less than full sized palette --- src/ImageSharp/Formats/Bmp/BmpCompression.cs | 10 +++++-- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 17 ++++++++++-- .../Formats/Bmp/BmpDecoderTests.cs | 26 +++++++++++++++++++ tests/ImageSharp.Tests/TestImages.cs | 2 ++ tests/Images/Input/Bmp/pal8os2sp.bmp | 3 +++ tests/Images/Input/Bmp/rgba32abf.bmp | 3 +++ 6 files changed, 57 insertions(+), 4 deletions(-) create mode 100644 tests/Images/Input/Bmp/pal8os2sp.bmp create mode 100644 tests/Images/Input/Bmp/rgba32abf.bmp diff --git a/src/ImageSharp/Formats/Bmp/BmpCompression.cs b/src/ImageSharp/Formats/Bmp/BmpCompression.cs index 5f14d22436..be275019e6 100644 --- a/src/ImageSharp/Formats/Bmp/BmpCompression.cs +++ b/src/ImageSharp/Formats/Bmp/BmpCompression.cs @@ -34,7 +34,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// If the first byte is zero, the record has different meanings, depending /// on the second byte. If the second byte is zero, it is the end of the row, /// if it is one, it is the end of the image. - /// Not supported at the moment. /// RLE4 = 2, @@ -54,6 +53,13 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// The bitmap contains a PNG image. /// Not supported at the moment. ///
- PNG = 5 + PNG = 5, + + /// + /// Introduced with Windows CE. + /// Specifies that the bitmap is not compressed and that the color table consists of four DWORD color + /// masks that specify the red, green, blue, and alpha components of each pixel. + /// + BI_ALPHABITFIELDS = 6 } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 496a6df4a2..d33ada2864 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -75,7 +75,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// /// The file header containing general information. - /// TODO: Why is this not used? We advance the stream but do not use the values parsed. /// private BmpFileHeader fileHeader; @@ -163,6 +162,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp break; case BmpCompression.BitFields: + case BmpCompression.BI_ALPHABITFIELDS: this.ReadBitFields(pixels, inverted); break; @@ -947,7 +947,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp infoHeaderType = BmpInfoHeaderType.WinVersion3; this.infoHeader = BmpInfoHeader.ParseV3(buffer); - // if the info header is BMP version 3 and the compression type is BITFIELDS, + // If the info header is BMP version 3 and the compression type is BITFIELDS, // color masks for each color channel follow the info header. if (this.infoHeader.Compression == BmpCompression.BitFields) { @@ -958,6 +958,16 @@ namespace SixLabors.ImageSharp.Formats.Bmp this.infoHeader.GreenMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)); this.infoHeader.BlueMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)); } + else if (this.infoHeader.Compression == BmpCompression.BI_ALPHABITFIELDS) + { + byte[] bitfieldsBuffer = new byte[16]; + this.stream.Read(bitfieldsBuffer, 0, 16); + Span data = bitfieldsBuffer.AsSpan(); + this.infoHeader.RedMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4)); + this.infoHeader.GreenMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)); + this.infoHeader.BlueMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)); + this.infoHeader.AlphaMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(12, 4)); + } } else if (headerSize == BmpInfoHeader.AdobeV3Size) { @@ -1078,6 +1088,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp int colorMapSizeBytes = this.fileHeader.Offset - BmpFileHeader.Size - this.infoHeader.HeaderSize; int colorCountForBitDepth = ImageMaths.GetColorCountForBitDepth(this.infoHeader.BitsPerPixel); bytesPerColorMapEntry = colorMapSizeBytes / colorCountForBitDepth; + + // Edge case for less-than-full-sized palette: bytesPerColorMapEntry should be at least 3. + bytesPerColorMapEntry = Math.Max(bytesPerColorMapEntry, 3); colorMapSize = colorMapSizeBytes; } } diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index e7b5aeaeae..ad47ed1e7e 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -60,6 +60,20 @@ namespace SixLabors.ImageSharp.Tests } } + [Theory] + [WithFile(RgbaAlphaBitfields, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecodeAlphaBitfields(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder())) + { + image.DebugSave(provider); + + // TODO: Neither System.Drawing nor MagickReferenceDecoder decode this file. + // image.CompareToOriginal(provider); + } + } + [Theory] [WithFile(Bit32Rgba, PixelTypes.Rgba32)] public void BmpDecoder_CanDecodeBitmap_WithAlphaChannel(TestImageProvider provider) @@ -114,6 +128,18 @@ namespace SixLabors.ImageSharp.Tests } } + [Theory] + [WithFile(LessThanFullSizedPalette, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecodeLessThanFullPalete(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder())) + { + image.DebugSave(provider); + image.CompareToOriginal(provider, new MagickReferenceDecoder()); + } + } + [Theory] [WithFile(Rgba32bf56, PixelTypes.Rgba32)] public void BmpDecoder_CanDecodeAdobeBmpv3(TestImageProvider provider) diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 51e2d084c6..3a497683ad 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -221,6 +221,7 @@ namespace SixLabors.ImageSharp.Tests public const string Bit8Palette4 = "Bmp/pal8-0.bmp"; public const string Os2v2Short = "Bmp/pal8os2v2-16.bmp"; public const string Os2v2 = "Bmp/pal8os2v2.bmp"; + public const string LessThanFullSizedPalette = "Bmp/pal8os2sp.bmp"; public const string Pal8Offset = "Bmp/pal8offs.bmp"; // Bitmap images with compression type BITFIELDS @@ -232,6 +233,7 @@ namespace SixLabors.ImageSharp.Tests public const string Issue735 = "Bmp/issue735.bmp"; public const string Rgba32bf56 = "Bmp/rgba32h56.bmp"; public const string Rgba321010102 = "Bmp/rgba32-1010102.bmp"; + public const string RgbaAlphaBitfields = "Bmp/rgba32abf.bmp"; public static readonly string[] BitFields = { diff --git a/tests/Images/Input/Bmp/pal8os2sp.bmp b/tests/Images/Input/Bmp/pal8os2sp.bmp new file mode 100644 index 0000000000..7d3e89d84c --- /dev/null +++ b/tests/Images/Input/Bmp/pal8os2sp.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e58eb53dcbf1c0920ad5af8d07f576ee794c1c5d051e1d950060ab6f6f253f50 +size 8974 diff --git a/tests/Images/Input/Bmp/rgba32abf.bmp b/tests/Images/Input/Bmp/rgba32abf.bmp new file mode 100644 index 0000000000..0f4c4b76ec --- /dev/null +++ b/tests/Images/Input/Bmp/rgba32abf.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1d2183466e05c768b5a9d2ce5d132d72db2272bb331c7b27560d24f197f4165e +size 32582 From a33255c19c893b659066cecf5804d5121e497d26 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 11 Feb 2019 23:25:00 +1100 Subject: [PATCH 021/223] Reorder target frameworks (#833) --- src/ImageSharp.Drawing/ImageSharp.Drawing.csproj | 2 +- src/ImageSharp/ImageSharp.csproj | 2 +- tests/ImageSharp.Tests/ImageSharp.Tests.csproj | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj index f4906d25e3..638687ae55 100644 --- a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj +++ b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj @@ -10,7 +10,7 @@ $(packageversion) 0.0.1 - netstandard1.3;netstandard2.0 + netcoreapp2.1;netstandard1.3;netstandard2.0 7.3 true true diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index 6ab609d5c0..f7f9e1d5f4 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -10,7 +10,7 @@ $(packageversion) 0.0.1 - netstandard1.3;netstandard2.0;netcoreapp2.1;net472 + netcoreapp2.1;netstandard1.3;netstandard2.0;net472 true true SixLabors.ImageSharp diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index e20f1514cc..73f97bab38 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -1,6 +1,6 @@  - net462;net472;netcoreapp2.1 + netcoreapp2.1;net462;net472 True latest full From dae7c7a2d88d887a8206e2183eccf4db2f8a568d Mon Sep 17 00:00:00 2001 From: Robin Krom Date: Fri, 15 Feb 2019 10:08:21 +0100 Subject: [PATCH 022/223] Some changes to make the usages of GDI in benchmarks and tests more stable, this should hopefully prevent "a generic error occurred in GDI+". Hopefully I didn't miss one... (#835) --- .../Drawing/DrawBeziers.cs | 16 +-- .../Drawing/DrawLines.cs | 14 ++- .../Drawing/DrawPolygon.cs | 14 ++- .../ImageSharp.Benchmarks/Drawing/DrawText.cs | 7 +- .../Drawing/DrawTextOutline.cs | 12 +- .../Drawing/FillWithPattern.cs | 6 +- .../ReferenceCodecs/SystemDrawingBridge.cs | 113 ++++++++++-------- 7 files changed, 104 insertions(+), 78 deletions(-) diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs index edbbceb628..34dd9d4dc8 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs @@ -26,13 +26,15 @@ namespace SixLabors.ImageSharp.Benchmarks { graphics.InterpolationMode = InterpolationMode.Default; graphics.SmoothingMode = SmoothingMode.AntiAlias; - Pen pen = new Pen(System.Drawing.Color.HotPink, 10); - graphics.DrawBeziers(pen, new[] { - new PointF(10, 500), - new PointF(30, 10), - new PointF(240, 30), - new PointF(300, 500) - }); + using (var pen = new Pen(System.Drawing.Color.HotPink, 10)) + { + graphics.DrawBeziers(pen, new[] { + new PointF(10, 500), + new PointF(30, 10), + new PointF(240, 30), + new PointF(300, 500) + }); + } } using (MemoryStream ms = new MemoryStream()) diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs index 8946835993..091f367622 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs @@ -26,12 +26,14 @@ namespace SixLabors.ImageSharp.Benchmarks { graphics.InterpolationMode = InterpolationMode.Default; graphics.SmoothingMode = SmoothingMode.AntiAlias; - var pen = new Pen(System.Drawing.Color.HotPink, 10); - graphics.DrawLines(pen, new[] { - new PointF(10, 10), - new PointF(550, 50), - new PointF(200, 400) - }); + using (var pen = new Pen(System.Drawing.Color.HotPink, 10)) + { + graphics.DrawLines(pen, new[] { + new PointF(10, 10), + new PointF(550, 50), + new PointF(200, 400) + }); + } } using (var ms = new MemoryStream()) diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs index 5fbd9f1123..ab8a7134e7 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs @@ -26,12 +26,14 @@ namespace SixLabors.ImageSharp.Benchmarks { graphics.InterpolationMode = InterpolationMode.Default; graphics.SmoothingMode = SmoothingMode.AntiAlias; - Pen pen = new Pen(System.Drawing.Color.HotPink, 10); - graphics.DrawPolygon(pen, new[] { - new PointF(10, 10), - new PointF(550, 50), - new PointF(200, 400) - }); + using (var pen = new Pen(System.Drawing.Color.HotPink, 10)) + { + graphics.DrawPolygon(pen, new[] { + new PointF(10, 10), + new PointF(550, 50), + new PointF(200, 400) + }); + } } using (MemoryStream ms = new MemoryStream()) diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawText.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawText.cs index 624b54278a..60c58dadc9 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/DrawText.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/DrawText.cs @@ -35,9 +35,10 @@ namespace SixLabors.ImageSharp.Benchmarks { graphics.InterpolationMode = InterpolationMode.Default; graphics.SmoothingMode = SmoothingMode.AntiAlias; - Pen pen = new Pen(System.Drawing.Color.HotPink, 10); - var font = new Font("Arial", 12, GraphicsUnit.Point); - graphics.DrawString(TextToRender, font, System.Drawing.Brushes.HotPink, new RectangleF(10, 10, 780, 780)); + using (var font = new Font("Arial", 12, GraphicsUnit.Point)) + { + graphics.DrawString(TextToRender, font, System.Drawing.Brushes.HotPink, new RectangleF(10, 10, 780, 780)); + } } } } diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawTextOutline.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawTextOutline.cs index ba6d055e37..2d6505a91f 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/DrawTextOutline.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/DrawTextOutline.cs @@ -34,11 +34,13 @@ namespace SixLabors.ImageSharp.Benchmarks { graphics.InterpolationMode = InterpolationMode.Default; graphics.SmoothingMode = SmoothingMode.AntiAlias; - Pen pen = new Pen(System.Drawing.Color.HotPink, 10); - var font = new Font("Arial", 12, GraphicsUnit.Point); - var gp = new GraphicsPath(); - gp.AddString(TextToRender, font.FontFamily, (int)font.Style, font.Size, new RectangleF(10, 10, 780, 780), new StringFormat()); - graphics.DrawPath(pen, gp); + using (var pen = new Pen(System.Drawing.Color.HotPink, 10)) + using (var font = new Font("Arial", 12, GraphicsUnit.Point)) + using (var gp = new GraphicsPath()) + { + gp.AddString(TextToRender, font.FontFamily, (int)font.Style, font.Size, new RectangleF(10, 10, 780, 780), new StringFormat()); + graphics.DrawPath(pen, gp); + } } } } diff --git a/tests/ImageSharp.Benchmarks/Drawing/FillWithPattern.cs b/tests/ImageSharp.Benchmarks/Drawing/FillWithPattern.cs index 5f8f2ff064..3723619882 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/FillWithPattern.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/FillWithPattern.cs @@ -26,8 +26,10 @@ namespace SixLabors.ImageSharp.Benchmarks using (Graphics graphics = Graphics.FromImage(destination)) { graphics.SmoothingMode = SmoothingMode.AntiAlias; - HatchBrush brush = new HatchBrush(HatchStyle.BackwardDiagonal, Color.HotPink); - graphics.FillRectangle(brush, new Rectangle(0, 0, 800, 800)); // can't find a way to flood fill with a brush + using (var brush = new HatchBrush(HatchStyle.BackwardDiagonal, Color.HotPink)) + { + graphics.FillRectangle(brush, new Rectangle(0, 0, 800, 800)); // can't find a way to flood fill with a brush + } } using (MemoryStream ms = new MemoryStream()) { diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs index 7e87c23db5..79c19f2be7 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs @@ -39,32 +39,39 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs } BitmapData data = bmp.LockBits(fullRect, ImageLockMode.ReadWrite, bmp.PixelFormat); - byte* sourcePtrBase = (byte*)data.Scan0; + var image = new Image(w, h); + try + { + byte* sourcePtrBase = (byte*)data.Scan0; - long sourceRowByteCount = data.Stride; - long destRowByteCount = w * sizeof(Bgra32); + long sourceRowByteCount = data.Stride; + long destRowByteCount = w * sizeof(Bgra32); - var image = new Image(w, h); - Configuration configuration = image.GetConfiguration(); + Configuration configuration = image.GetConfiguration(); - using (IMemoryOwner workBuffer = Configuration.Default.MemoryAllocator.Allocate(w)) - { - fixed (Bgra32* destPtr = &workBuffer.GetReference()) + using (IMemoryOwner workBuffer = Configuration.Default.MemoryAllocator.Allocate(w)) { - for (int y = 0; y < h; y++) + fixed (Bgra32* destPtr = &workBuffer.GetReference()) { - Span row = image.Frames.RootFrame.GetPixelRowSpan(y); - - byte* sourcePtr = sourcePtrBase + (data.Stride * y); - - Buffer.MemoryCopy(sourcePtr, destPtr, destRowByteCount, sourceRowByteCount); - PixelOperations.Instance.FromBgra32( - configuration, - workBuffer.GetSpan().Slice(0, w), - row); + for (int y = 0; y < h; y++) + { + Span row = image.Frames.RootFrame.GetPixelRowSpan(y); + + byte* sourcePtr = sourcePtrBase + (data.Stride * y); + + Buffer.MemoryCopy(sourcePtr, destPtr, destRowByteCount, sourceRowByteCount); + PixelOperations.Instance.FromBgra32( + configuration, + workBuffer.GetSpan().Slice(0, w), + row); + } } } } + finally + { + bmp.UnlockBits(data); + } return image; } @@ -91,33 +98,36 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs } BitmapData data = bmp.LockBits(fullRect, ImageLockMode.ReadWrite, bmp.PixelFormat); - byte* sourcePtrBase = (byte*)data.Scan0; + var image = new Image(w, h); + try + { + byte* sourcePtrBase = (byte*)data.Scan0; - long sourceRowByteCount = data.Stride; - long destRowByteCount = w * sizeof(Bgr24); + long sourceRowByteCount = data.Stride; + long destRowByteCount = w * sizeof(Bgr24); - var image = new Image(w, h); - Configuration configuration = image.GetConfiguration(); + Configuration configuration = image.GetConfiguration(); - using (IMemoryOwner workBuffer = Configuration.Default.MemoryAllocator.Allocate(w)) - { - fixed (Bgr24* destPtr = &workBuffer.GetReference()) + using (IMemoryOwner workBuffer = Configuration.Default.MemoryAllocator.Allocate(w)) { - for (int y = 0; y < h; y++) + fixed (Bgr24* destPtr = &workBuffer.GetReference()) { - Span row = image.Frames.RootFrame.GetPixelRowSpan(y); + for (int y = 0; y < h; y++) + { + Span row = image.Frames.RootFrame.GetPixelRowSpan(y); - byte* sourcePtr = sourcePtrBase + (data.Stride * y); + byte* sourcePtr = sourcePtrBase + (data.Stride * y); - Buffer.MemoryCopy(sourcePtr, destPtr, destRowByteCount, sourceRowByteCount); - PixelOperations.Instance.FromBgr24( - configuration, - workBuffer.GetSpan().Slice(0, w), - row); + Buffer.MemoryCopy(sourcePtr, destPtr, destRowByteCount, sourceRowByteCount); + PixelOperations.Instance.FromBgr24(configuration, workBuffer.GetSpan().Slice(0, w), row); + } } } } - + finally + { + bmp.UnlockBits(data); + } return image; } @@ -131,27 +141,32 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs var resultBitmap = new Bitmap(w, h, PixelFormat.Format32bppArgb); var fullRect = new Rectangle(0, 0, w, h); BitmapData data = resultBitmap.LockBits(fullRect, ImageLockMode.ReadWrite, resultBitmap.PixelFormat); - byte* destPtrBase = (byte*)data.Scan0; + try + { + byte* destPtrBase = (byte*)data.Scan0; - long destRowByteCount = data.Stride; - long sourceRowByteCount = w * sizeof(Bgra32); + long destRowByteCount = data.Stride; + long sourceRowByteCount = w * sizeof(Bgra32); - using (IMemoryOwner workBuffer = image.GetConfiguration().MemoryAllocator.Allocate(w)) - { - fixed (Bgra32* sourcePtr = &workBuffer.GetReference()) + using (IMemoryOwner workBuffer = image.GetConfiguration().MemoryAllocator.Allocate(w)) { - for (int y = 0; y < h; y++) + fixed (Bgra32* sourcePtr = &workBuffer.GetReference()) { - Span row = image.Frames.RootFrame.GetPixelRowSpan(y); - PixelOperations.Instance.ToBgra32(configuration, row, workBuffer.GetSpan()); - byte* destPtr = destPtrBase + (data.Stride * y); - - Buffer.MemoryCopy(sourcePtr, destPtr, destRowByteCount, sourceRowByteCount); + for (int y = 0; y < h; y++) + { + Span row = image.Frames.RootFrame.GetPixelRowSpan(y); + PixelOperations.Instance.ToBgra32(configuration, row, workBuffer.GetSpan()); + byte* destPtr = destPtrBase + (data.Stride * y); + + Buffer.MemoryCopy(sourcePtr, destPtr, destRowByteCount, sourceRowByteCount); + } } } } - - resultBitmap.UnlockBits(data); + finally + { + resultBitmap.UnlockBits(data); + } return resultBitmap; } From 19270664b0e386e718c582759218bd606cbeef32 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 15 Feb 2019 21:55:16 +1100 Subject: [PATCH 023/223] Remove hardcoded subsampling. Fix #817 (#834) --- .../Jpeg/Components/Decoder/JpegComponent.cs | 38 +++++-------------- .../Formats/Jpg/JpegDecoderTests.Images.cs | 1 + tests/ImageSharp.Tests/TestImages.cs | 3 +- tests/Images/External | 2 +- .../Jpg/baseline/ycck-subsample-1222.jpg | 3 ++ 5 files changed, 17 insertions(+), 30 deletions(-) create mode 100644 tests/Images/Input/Jpg/baseline/ycck-subsample-1222.jpg diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs index ef03582d69..cfed5d535f 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs @@ -2,8 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; using SixLabors.Memory; @@ -12,9 +10,9 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { /// - /// Represents a single frame component + /// Represents a single frame component. /// - internal class JpegComponent : IDisposable, IJpegComponent + internal sealed class JpegComponent : IDisposable, IJpegComponent { private readonly MemoryAllocator memoryAllocator; @@ -31,12 +29,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder } /// - /// Gets the component Id + /// Gets the component Id. /// public byte Id { get; } /// - /// Gets or sets DC coefficient predictor + /// Gets or sets DC coefficient predictor. /// public int DcPredictor { get; set; } @@ -69,22 +67,22 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder public Size SamplingFactors { get; set; } /// - /// Gets the number of blocks per line + /// Gets the number of blocks per line. /// public int WidthInBlocks { get; private set; } /// - /// Gets the number of blocks per column + /// Gets the number of blocks per column. /// public int HeightInBlocks { get; private set; } /// - /// Gets or sets the index for the DC Huffman table + /// Gets or sets the index for the DC Huffman table. /// public int DCHuffmanTableId { get; set; } /// - /// Gets or sets the index for the AC Huffman table + /// Gets or sets the index for the AC Huffman table. /// public int ACHuffmanTableId { get; set; } @@ -109,24 +107,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder int blocksPerColumnForMcu = this.Frame.McusPerColumn * this.VerticalSamplingFactor; this.SizeInBlocks = new Size(blocksPerLineForMcu, blocksPerColumnForMcu); - // For 4-component images (either CMYK or YCbCrK), we only support two - // hv vectors: [0x11 0x11 0x11 0x11] and [0x22 0x11 0x11 0x22]. - // Theoretically, 4-component JPEG images could mix and match hv values - // but in practice, those two combinations are the only ones in use, - // and it simplifies the applyBlack code below if we can assume that: - // - for CMYK, the C and K channels have full samples, and if the M - // and Y channels subsample, they subsample both horizontally and - // vertically. - // - for YCbCrK, the Y and K channels have full samples. - if (this.Index == 0 || this.Index == 3) - { - this.SubSamplingDivisors = new Size(1, 1); - } - else - { - JpegComponent c0 = this.Frame.Components[0]; - this.SubSamplingDivisors = c0.SamplingFactors.DivideBy(this.SamplingFactors); - } + JpegComponent c0 = this.Frame.Components[0]; + this.SubSamplingDivisors = c0.SamplingFactors.DivideBy(this.SamplingFactors); int totalNumberOfBlocks = blocksPerColumnForMcu * (blocksPerLineForMcu + 1); int width = this.WidthInBlocks + 1; diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs index 03f1826edd..9f34d7d09b 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs @@ -21,6 +21,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg TestImages.Jpeg.Baseline.Jpeg444, TestImages.Jpeg.Baseline.Bad.BadEOF, TestImages.Jpeg.Baseline.MultiScanBaselineCMYK, + TestImages.Jpeg.Baseline.YcckSubsample1222, TestImages.Jpeg.Baseline.Bad.BadRST, TestImages.Jpeg.Issues.MultiHuffmanBaseline394, TestImages.Jpeg.Issues.ExifDecodeOutOfRange694, diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 3a497683ad..e28caf6277 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -140,13 +140,14 @@ namespace SixLabors.ImageSharp.Tests public const string MultiScanBaselineCMYK = "Jpg/baseline/MultiScanBaselineCMYK.jpg"; public const string Ratio1x1 = "Jpg/baseline/ratio-1x1.jpg"; public const string Testorig12bit = "Jpg/baseline/testorig12.jpg"; + public const string YcckSubsample1222 = "Jpg/baseline/ycck-subsample-1222.jpg"; public static readonly string[] All = { Cmyk, Ycck, Exif, Floorplan, Calliphora, Turtle, GammaDalaiLamaGray, Hiyamugi, Jpeg400, Jpeg420Exif, Jpeg444, - Ratio1x1, Testorig12bit + Ratio1x1, Testorig12bit, YcckSubsample1222 }; } diff --git a/tests/Images/External b/tests/Images/External index 74995302e3..32dc8aec11 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 74995302e3c9a5913f1cf09d71b15f888d0ec022 +Subproject commit 32dc8aec1109b681f056264e6bde1f3a76f08aba diff --git a/tests/Images/Input/Jpg/baseline/ycck-subsample-1222.jpg b/tests/Images/Input/Jpg/baseline/ycck-subsample-1222.jpg new file mode 100644 index 0000000000..17c23f1358 --- /dev/null +++ b/tests/Images/Input/Jpg/baseline/ycck-subsample-1222.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ca7c16a2984d14ebc483780d9d1e8c131eca01d89f53512cc7add538c93a38c6 +size 31527 From 1054f842531ae2f6078e10a86c259ec24307345a Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 17 Feb 2019 22:05:16 +1100 Subject: [PATCH 024/223] Jpeg Fuzz Fixes (#836) * Nomalize jpeg exceptions. Fix #821 * Fix #822 * Fix #823 * Check for correct QT index. Touch #824 * Check DHT props. Touch #824 * Limit sampling factors to 1 & 2. Touch #824 * Add already fixed image 4. Touch #824 * Check for excessive code lengths. Touch #824 * Add already fixed image 6. Touch #824 * Lint progressive scan details. Touch #824 * Add already fixed image 8. Fix #824 * Remove duplicate per-block checks * Add already fixed image 1. Touch #825 * Don't throw on bad JFIF density units. * Add already fixed image 3. Touch #825 * Add already fixed image 4. Fix #825 * Check SOFn marker length. Touch #826 * Add already fixed image 2. Touch #826 * Add already fixed image 3. Fix #826 * Add fixed already fixed image. Fix #827 * Revert unneeded bounds check introduced in #804 --- .../Jpeg/Components/Decoder/HuffmanTable.cs | 22 ++--- .../Jpeg/Components/Decoder/JFifMarker.cs | 19 ++-- .../Jpeg/Components/Decoder/JpegComponent.cs | 21 +++++ .../Jpeg/Components/Decoder/JpegFrame.cs | 2 +- .../Jpeg/Components/Decoder/ScanDecoder.cs | 62 +++++++++++-- .../Formats/Jpeg/JpegDecoderCore.cs | 92 ++++++++++++++----- .../Formats/Jpeg/JpegThrowHelper.cs | 35 ++++--- .../Formats/Jpg/JpegDecoderTests.Baseline.cs | 19 +--- .../Formats/Jpg/JpegDecoderTests.Images.cs | 28 ++++++ tests/ImageSharp.Tests/TestImages.cs | 29 +++++- tests/Images/External | 2 +- ...ticalEOF .jpg => Issue214-CriticalEOF.jpg} | 0 .../Issue797-NullReferenceException.jpg} | 0 .../Issue798-AccessViolationException.jpg | 0 .../fuzz/Issue821-DivideByZeroException.jpg | 3 + .../fuzz/Issue822-DivideByZeroException.jpg | 3 + .../fuzz/Issue823-NullReferenceException.jpg | 3 + .../Issue824-IndexOutOfRangeException-A.jpg | 3 + .../Issue824-IndexOutOfRangeException-B.jpg | 3 + .../Issue824-IndexOutOfRangeException-C.jpg | 3 + .../Issue824-IndexOutOfRangeException-D.jpg | 3 + .../Issue824-IndexOutOfRangeException-E.jpg | 3 + .../Issue824-IndexOutOfRangeException-F.jpg | 3 + .../Issue824-IndexOutOfRangeException-G.jpg | 3 + .../Issue824-IndexOutOfRangeException-H.jpg | 3 + ...Issue825-ArgumentOutOfRangeException-A.jpg | 3 + ...Issue825-ArgumentOutOfRangeException-B.jpg | 3 + ...Issue825-ArgumentOutOfRangeException-C.jpg | 3 + ...Issue825-ArgumentOutOfRangeException-D.jpg | 3 + .../fuzz/Issue826-ArgumentException-A.jpg | 3 + .../fuzz/Issue826-ArgumentException-B.jpg | 3 + .../fuzz/Issue826-ArgumentException-C.jpg | 3 + .../Issue827-AccessViolationException.jpg | 3 + 33 files changed, 303 insertions(+), 85 deletions(-) rename tests/Images/Input/Jpg/issues/{Issue214-CriticalEOF .jpg => Issue214-CriticalEOF.jpg} (100%) rename tests/Images/Input/Jpg/issues/{Issue797-InvalidImage.jpg => fuzz/Issue797-NullReferenceException.jpg} (100%) rename tests/Images/Input/Jpg/issues/{ => fuzz}/Issue798-AccessViolationException.jpg (100%) create mode 100644 tests/Images/Input/Jpg/issues/fuzz/Issue821-DivideByZeroException.jpg create mode 100644 tests/Images/Input/Jpg/issues/fuzz/Issue822-DivideByZeroException.jpg create mode 100644 tests/Images/Input/Jpg/issues/fuzz/Issue823-NullReferenceException.jpg create mode 100644 tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-A.jpg create mode 100644 tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-B.jpg create mode 100644 tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-C.jpg create mode 100644 tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-D.jpg create mode 100644 tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-E.jpg create mode 100644 tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-F.jpg create mode 100644 tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-G.jpg create mode 100644 tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-H.jpg create mode 100644 tests/Images/Input/Jpg/issues/fuzz/Issue825-ArgumentOutOfRangeException-A.jpg create mode 100644 tests/Images/Input/Jpg/issues/fuzz/Issue825-ArgumentOutOfRangeException-B.jpg create mode 100644 tests/Images/Input/Jpg/issues/fuzz/Issue825-ArgumentOutOfRangeException-C.jpg create mode 100644 tests/Images/Input/Jpg/issues/fuzz/Issue825-ArgumentOutOfRangeException-D.jpg create mode 100644 tests/Images/Input/Jpg/issues/fuzz/Issue826-ArgumentException-A.jpg create mode 100644 tests/Images/Input/Jpg/issues/fuzz/Issue826-ArgumentException-B.jpg create mode 100644 tests/Images/Input/Jpg/issues/fuzz/Issue826-ArgumentException-C.jpg create mode 100644 tests/Images/Input/Jpg/issues/fuzz/Issue827-AccessViolationException.jpg diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs index 9e11981b12..90b3624651 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs @@ -50,10 +50,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// The huffman values public HuffmanTable(MemoryAllocator memoryAllocator, ReadOnlySpan codeLengths, ReadOnlySpan values) { - // We do some bounds checks in the code here to protect against AccessViolationExceptions - const int HuffCodeLength = 257; - const int MaxSizeLength = HuffCodeLength - 1; - using (IMemoryOwner huffcode = memoryAllocator.Allocate(HuffCodeLength)) + const int Length = 257; + using (IMemoryOwner huffcode = memoryAllocator.Allocate(Length)) { ref short huffcodeRef = ref MemoryMarshal.GetReference(huffcode.GetSpan()); ref byte codeLengthsRef = ref MemoryMarshal.GetReference(codeLengths); @@ -65,7 +63,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder for (short i = 1; i < 17; i++) { byte length = Unsafe.Add(ref codeLengthsRef, i); - for (short j = 0; j < length && x < MaxSizeLength; j++) + for (short j = 0; j < length; j++) { Unsafe.Add(ref sizesRef, x++) = i; } @@ -86,7 +84,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder Unsafe.Add(ref valOffsetRef, k) = (int)(si - code); if (Unsafe.Add(ref sizesRef, si) == k) { - while (Unsafe.Add(ref sizesRef, si) == k && si < HuffCodeLength) + while (Unsafe.Add(ref sizesRef, si) == k) { Unsafe.Add(ref huffcodeRef, si++) = (short)code++; } @@ -103,19 +101,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder // Generate non-spec lookup tables to speed up decoding. const int FastBits = ScanDecoder.FastBits; ref byte lookaheadRef = ref this.Lookahead[0]; - const uint MaxFastLength = 1 << FastBits; - Unsafe.InitBlockUnaligned(ref lookaheadRef, 0xFF, MaxFastLength); // Flag for non-accelerated + Unsafe.InitBlockUnaligned(ref lookaheadRef, 0xFF, 1 << FastBits); // Flag for non-accelerated for (int i = 0; i < si; i++) { int size = Unsafe.Add(ref sizesRef, i); if (size <= FastBits) { - int huffCode = Unsafe.Add(ref huffcodeRef, i) << (FastBits - size); - int max = 1 << (FastBits - size); - for (int left = 0; left < max; left++) + int fastOffset = FastBits - size; + int fastCode = Unsafe.Add(ref huffcodeRef, i) << fastOffset; + int fastMax = 1 << fastOffset; + for (int left = 0; left < fastMax; left++) { - Unsafe.Add(ref lookaheadRef, huffCode + left) = (byte)i; + Unsafe.Add(ref lookaheadRef, fastCode + left) = (byte)i; } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs index c51a2f4da5..76f0e2cb51 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs @@ -27,12 +27,20 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// The vertical pixel density private JFifMarker(byte majorVersion, byte minorVersion, byte densityUnits, short xDensity, short yDensity) { - Guard.MustBeGreaterThan(xDensity, 0, nameof(xDensity)); - Guard.MustBeGreaterThan(yDensity, 0, nameof(yDensity)); - Guard.MustBeBetweenOrEqualTo(densityUnits, 0, 2, nameof(densityUnits)); + if (xDensity <= 0) + { + JpegThrowHelper.ThrowImageFormatException($"X-Density {xDensity} must be greater than 0."); + } + + if (yDensity <= 0) + { + JpegThrowHelper.ThrowImageFormatException($"Y-Density {yDensity} must be greater than 0."); + } this.MajorVersion = majorVersion; this.MinorVersion = minorVersion; + + // LibJpeg and co will simply cast and not try to enforce a range. this.DensityUnits = (PixelResolutionUnit)densityUnits; this.XDensity = xDensity; this.YDensity = yDensity; @@ -104,10 +112,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder } /// - public override bool Equals(object obj) - { - return obj is JFifMarker other && this.Equals(other); - } + public override bool Equals(object obj) => obj is JFifMarker other && this.Equals(other); /// public override int GetHashCode() diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs index cfed5d535f..ac24f4ca58 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs @@ -21,9 +21,25 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder this.memoryAllocator = memoryAllocator; this.Frame = frame; this.Id = id; + + // Valid sampling factors are 1..2 + if (horizontalFactor == 0 + || verticalFactor == 0 + || horizontalFactor > 2 + || verticalFactor > 2) + { + JpegThrowHelper.ThrowBadSampling(); + } + this.HorizontalSamplingFactor = horizontalFactor; this.VerticalSamplingFactor = verticalFactor; this.SamplingFactors = new Size(this.HorizontalSamplingFactor, this.VerticalSamplingFactor); + + if (quantizationTableIndex > 3) + { + JpegThrowHelper.ThrowBadQuantizationTable(); + } + this.QuantizationTableIndex = quantizationTableIndex; this.Index = index; } @@ -110,6 +126,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder JpegComponent c0 = this.Frame.Components[0]; this.SubSamplingDivisors = c0.SamplingFactors.DivideBy(this.SamplingFactors); + if (this.SubSamplingDivisors.Width == 0 || this.SubSamplingDivisors.Height == 0) + { + JpegThrowHelper.ThrowBadSampling(); + } + int totalNumberOfBlocks = blocksPerColumnForMcu * (blocksPerLineForMcu + 1); int width = this.WidthInBlocks + 1; int height = totalNumberOfBlocks / width; diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs index 36a3dc2d26..33a7c68baf 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs @@ -83,7 +83,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { for (int i = 0; i < this.Components.Length; i++) { - this.Components[i].Dispose(); + this.Components[i]?.Dispose(); } this.Components = null; diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs index a1c1b023e4..070652a9f9 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs @@ -270,8 +270,60 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder } } + private void CheckProgressiveData() + { + // Validate successive scan parameters. + // Logic has been adapted from libjpeg. + // See Table B.3 – Scan header parameter size and values. itu-t81.pdf + bool invalid = false; + if (this.spectralStart == 0) + { + if (this.spectralEnd != 0) + { + invalid = true; + } + } + else + { + // Need not check Ss/Se < 0 since they came from unsigned bytes. + if (this.spectralEnd < this.spectralStart || this.spectralEnd > 63) + { + invalid = true; + } + + // AC scans may have only one component. + if (this.componentsLength != 1) + { + invalid = true; + } + } + + if (this.successiveHigh != 0) + { + // Successive approximation refinement scan: must have Al = Ah-1. + if (this.successiveHigh - 1 != this.successiveLow) + { + invalid = true; + } + } + + // TODO: How does this affect 12bit jpegs. + // According to libjpeg the range covers 8bit only? + if (this.successiveLow > 13) + { + invalid = true; + } + + if (invalid) + { + JpegThrowHelper.ThrowBadProgressiveScan(this.spectralStart, this.spectralEnd, this.successiveHigh, this.successiveLow); + } + } + private void ParseProgressiveData() { + this.CheckProgressiveData(); + if (this.componentsLength == 1) { this.ParseProgressiveDataNonInterleaved(); @@ -483,11 +535,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder ref Block8x8 block, ref HuffmanTable dcTable) { - if (this.spectralEnd != 0) - { - JpegThrowHelper.ThrowImageFormatException("Can't merge DC and AC."); - } - this.CheckBits(); ref short blockDataRef = ref Unsafe.As(ref block); @@ -518,11 +565,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder ref HuffmanTable acTable, ref short fastACRef) { - if (this.spectralStart == 0) - { - JpegThrowHelper.ThrowImageFormatException("Can't merge DC and AC."); - } - ref short blockDataRef = ref Unsafe.As(ref block); if (this.successiveHigh == 0) diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 06c844d58e..9bcfd9ff64 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -255,7 +255,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg var fileMarker = new JpegFileMarker(this.markerBuffer[1], 0); if (fileMarker.Marker != JpegConstants.Markers.SOI) { - throw new ImageFormatException("Missing SOI marker."); + JpegThrowHelper.ThrowImageFormatException("Missing SOI marker."); } this.InputStream.Read(this.markerBuffer, 0, 2); @@ -419,7 +419,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg : JpegColorSpace.Cmyk; } - throw new ImageFormatException($"Unsupported color mode. Max components 4; found {this.ComponentCount}"); + JpegThrowHelper.ThrowImageFormatException($"Unsupported color mode. Max components 4; found {this.ComponentCount}"); + return default; } /// @@ -646,6 +647,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg bool done = false; remaining--; int quantizationTableSpec = this.InputStream.ReadByte(); + int tableIndex = quantizationTableSpec & 15; + + // Max index. 4 Tables max. + if (tableIndex > 3) + { + JpegThrowHelper.ThrowBadQuantizationTable(); + } switch (quantizationTableSpec >> 4) { @@ -661,7 +669,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg this.InputStream.Read(this.temp, 0, 64); remaining -= 64; - ref Block8x8F table = ref this.QuantizationTables[quantizationTableSpec & 15]; + ref Block8x8F table = ref this.QuantizationTables[tableIndex]; for (int j = 0; j < 64; j++) { table[j] = this.temp[j]; @@ -681,7 +689,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg this.InputStream.Read(this.temp, 0, 128); remaining -= 128; - ref Block8x8F table = ref this.QuantizationTables[quantizationTableSpec & 15]; + ref Block8x8F table = ref this.QuantizationTables[tableIndex]; for (int j = 0; j < 64; j++) { table[j] = (this.temp[2 * j] << 8) | this.temp[(2 * j) + 1]; @@ -690,7 +698,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg break; default: - throw new ImageFormatException("Bad Tq index value"); + JpegThrowHelper.ThrowBadQuantizationTable(); + break; } if (done) @@ -701,7 +710,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg if (remaining != 0) { - throw new ImageFormatException("DQT has wrong length"); + JpegThrowHelper.ThrowBadMarker(nameof(JpegConstants.Markers.DQT), remaining); } this.MetaData.GetFormatMetaData(JpegFormat.Instance).Quality = QualityEvaluator.EstimateQuality(this.QuantizationTables); @@ -717,15 +726,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg { if (this.Frame != null) { - throw new ImageFormatException("Multiple SOF markers. Only single frame jpegs supported."); + JpegThrowHelper.ThrowImageFormatException("Multiple SOF markers. Only single frame jpegs supported."); } - this.InputStream.Read(this.temp, 0, remaining); + // Read initial marker definitions. + const int length = 6; + this.InputStream.Read(this.temp, 0, length); // We only support 8-bit and 12-bit precision. if (!this.supportedPrecisions.Contains(this.temp[0])) { - throw new ImageFormatException("Only 8-Bit and 12-Bit precision supported."); + JpegThrowHelper.ThrowImageFormatException("Only 8-Bit and 12-Bit precision supported."); } this.Precision = this.temp[0]; @@ -740,22 +751,35 @@ namespace SixLabors.ImageSharp.Formats.Jpeg ComponentCount = this.temp[5] }; - this.ImageSizeInPixels = new Size(this.Frame.SamplesPerLine, this.Frame.Scanlines); - - int maxH = 0; - int maxV = 0; - int index = 6; + if (this.Frame.SamplesPerLine == 0 || this.Frame.Scanlines == 0) + { + JpegThrowHelper.ThrowInvalidImageDimensions(this.Frame.SamplesPerLine, this.Frame.Scanlines); + } + this.ImageSizeInPixels = new Size(this.Frame.SamplesPerLine, this.Frame.Scanlines); this.ComponentCount = this.Frame.ComponentCount; if (!metadataOnly) { + remaining -= length; + + const int componentBytes = 3; + if (remaining > this.ComponentCount * componentBytes) + { + JpegThrowHelper.ThrowBadMarker("SOFn", remaining); + } + + this.InputStream.Read(this.temp, 0, remaining); + // No need to pool this. They max out at 4 this.Frame.ComponentIds = new byte[this.ComponentCount]; this.Frame.ComponentOrder = new byte[this.ComponentCount]; this.Frame.Components = new JpegComponent[this.ComponentCount]; this.ColorSpace = this.DeduceJpegColorSpace(); + int maxH = 0; + int maxV = 0; + int index = 0; for (int i = 0; i < this.ComponentCount; i++) { byte hv = this.temp[index + 1]; @@ -777,7 +801,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg this.Frame.Components[i] = component; this.Frame.ComponentIds[i] = component.Id; - index += 3; + index += componentBytes; } this.Frame.MaxHorizontalFactor = maxH; @@ -795,12 +819,29 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// The remaining bytes in the segment block. private void ProcessDefineHuffmanTablesMarker(int remaining) { + int length = remaining; + using (IManagedByteBuffer huffmanData = this.configuration.MemoryAllocator.AllocateManagedByteBuffer(256, AllocationOptions.Clean)) { ref byte huffmanDataRef = ref MemoryMarshal.GetReference(huffmanData.GetSpan()); for (int i = 2; i < remaining;) { byte huffmanTableSpec = (byte)this.InputStream.ReadByte(); + int tableType = huffmanTableSpec >> 4; + int tableIndex = huffmanTableSpec & 15; + + // Types 0..1 DC..AC + if (tableType > 1) + { + JpegThrowHelper.ThrowImageFormatException("Bad Huffman Table type."); + } + + // Max tables of each type + if (tableIndex > 3) + { + JpegThrowHelper.ThrowImageFormatException("Bad Huffman Table index."); + } + this.InputStream.Read(huffmanData.Array, 0, 16); using (IManagedByteBuffer codeLengths = this.configuration.MemoryAllocator.AllocateManagedByteBuffer(17, AllocationOptions.Clean)) @@ -813,15 +854,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg codeLengthSum += Unsafe.Add(ref codeLengthsRef, j) = Unsafe.Add(ref huffmanDataRef, j - 1); } + length -= 17; + + if (codeLengthSum > 256 || codeLengthSum > length) + { + JpegThrowHelper.ThrowImageFormatException("Huffman table has excessive length."); + } + using (IManagedByteBuffer huffmanValues = this.configuration.MemoryAllocator.AllocateManagedByteBuffer(256, AllocationOptions.Clean)) { this.InputStream.Read(huffmanValues.Array, 0, codeLengthSum); i += 17 + codeLengthSum; - int tableType = huffmanTableSpec >> 4; - int tableIndex = huffmanTableSpec & 15; - this.BuildHuffmanTable( tableType == 0 ? this.dcHuffmanTables : this.acHuffmanTables, tableIndex, @@ -848,7 +893,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg { if (remaining != 2) { - throw new ImageFormatException($"DRI has wrong length: {remaining}"); + JpegThrowHelper.ThrowBadMarker(nameof(JpegConstants.Markers.DRI), remaining); } this.resetInterval = this.ReadUint16(); @@ -861,7 +906,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg { if (this.Frame is null) { - throw new ImageFormatException("No readable SOFn (Start Of Frame) marker found."); + JpegThrowHelper.ThrowImageFormatException("No readable SOFn (Start Of Frame) marker found."); } int selectorsCount = this.InputStream.ReadByte(); @@ -882,7 +927,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg if (componentIndex < 0) { - throw new ImageFormatException("Unknown component selector"); + JpegThrowHelper.ThrowImageFormatException($"Unknown component selector {componentIndex}."); } ref JpegComponent component = ref this.Frame.Components[componentIndex]; @@ -944,6 +989,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg private Image PostProcessIntoImage() where TPixel : struct, IPixel { + if (this.ImageWidth == 0 || this.ImageHeight == 0) + { + JpegThrowHelper.ThrowInvalidImageDimensions(this.ImageWidth, this.ImageHeight); + } + var image = Image.CreateUninitialized( this.configuration, this.ImageWidth, diff --git a/src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs b/src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs index c7f3666604..b30da28c45 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs @@ -8,19 +8,28 @@ namespace SixLabors.ImageSharp.Formats.Jpeg internal static class JpegThrowHelper { /// - /// Cold path optimization for throwing -s + /// Cold path optimization for throwing 's. /// - /// The error message for the exception - [MethodImpl(MethodImplOptions.NoInlining)] - public static void ThrowImageFormatException(string errorMessage) - { - throw new ImageFormatException(errorMessage); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static void ThrowBadHuffmanCode() - { - throw new ImageFormatException("Bad Huffman code."); - } + /// The error message for the exception. + [MethodImpl(InliningOptions.ColdPath)] + public static void ThrowImageFormatException(string errorMessage) => throw new ImageFormatException(errorMessage); + + [MethodImpl(InliningOptions.ColdPath)] + public static void ThrowBadMarker(string marker, int length) => throw new ImageFormatException($"Marker {marker} has bad length {length}."); + + [MethodImpl(InliningOptions.ColdPath)] + public static void ThrowBadQuantizationTable() => throw new ImageFormatException("Bad Quantization Table index."); + + [MethodImpl(InliningOptions.ColdPath)] + public static void ThrowBadHuffmanCode() => throw new ImageFormatException("Bad Huffman code."); + + [MethodImpl(InliningOptions.ColdPath)] + public static void ThrowBadSampling() => throw new ImageFormatException("Bad sampling factor."); + + [MethodImpl(InliningOptions.ColdPath)] + public static void ThrowBadProgressiveScan(int ss, int se, int ah, int al) => throw new ImageFormatException($"Invalid progressive parameters Ss={ss} Se={se} Ah={ah} Al={al}."); + + [MethodImpl(InliningOptions.ColdPath)] + public static void ThrowInvalidImageDimensions(int width, int height) => throw new ImageFormatException($"Invalid image dimensions: {width}x{height}."); } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs index 1c9d207cd1..31a5a0eeb0 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; using SixLabors.ImageSharp.PixelFormats; using Xunit; @@ -35,22 +34,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg } [Theory] - [WithFile(TestImages.Jpeg.Issues.CriticalEOF214, PixelTypes.Rgba32)] - public void DecodeBaselineJpeg_CriticalEOF_ShouldThrow(TestImageProvider provider) - where TPixel : struct, IPixel - { - // TODO: We need a public ImageDecoderException class in ImageSharp! - Assert.ThrowsAny(() => provider.GetImage(JpegDecoder)); - } - - [Theory] - [WithFile(TestImages.Jpeg.Issues.InvalidJpegThrowsWrongException797, PixelTypes.Rgba32)] - public void LoadingImage_InvalidTagLength_ShouldThrow(TestImageProvider provider) + [WithFileCollection(nameof(UnrecoverableTestJpegs), PixelTypes.Rgba32)] + public void UnrecoverableImagesShouldThrowCorrectError(TestImageProvider provider) where TPixel : struct, IPixel => Assert.Throws(() => provider.GetImage()); - - [Theory] - [WithFile(TestImages.Jpeg.Issues.AccessViolationException798, PixelTypes.Rgba32)] - public void LoadingImage_BadHuffman_ShouldNotThrow(TestImageProvider provider) - where TPixel : struct, IPixel => Assert.NotNull(provider.GetImage()); } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs index 9f34d7d09b..ff49de2481 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs @@ -31,6 +31,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg TestImages.Jpeg.Issues.ExifGetString750Load, TestImages.Jpeg.Issues.ExifGetString750Transform, + // LibJpeg can open this despite the invalid desity units. + TestImages.Jpeg.Issues.Fuzz.ArgumentOutOfRangeException825B, + // High depth images TestImages.Jpeg.Baseline.Testorig12bit, }; @@ -54,6 +57,31 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg TestImages.Jpeg.Issues.OrderedInterleavedProgressive723C }; + public static string[] UnrecoverableTestJpegs = { + + TestImages.Jpeg.Issues.CriticalEOF214, + TestImages.Jpeg.Issues.Fuzz.NullReferenceException797, + TestImages.Jpeg.Issues.Fuzz.AccessViolationException798, + TestImages.Jpeg.Issues.Fuzz.DivideByZeroException821, + TestImages.Jpeg.Issues.Fuzz.DivideByZeroException822, + TestImages.Jpeg.Issues.Fuzz.NullReferenceException823, + TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException824A, + TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException824B, + TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException824C, + TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException824D, + TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException824E, + TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException824F, + TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException824G, + TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException824H, + TestImages.Jpeg.Issues.Fuzz.ArgumentOutOfRangeException825A, + TestImages.Jpeg.Issues.Fuzz.ArgumentOutOfRangeException825C, + TestImages.Jpeg.Issues.Fuzz.ArgumentOutOfRangeException825D, + TestImages.Jpeg.Issues.Fuzz.ArgumentException826A, + TestImages.Jpeg.Issues.Fuzz.ArgumentException826B, + TestImages.Jpeg.Issues.Fuzz.ArgumentException826C, + TestImages.Jpeg.Issues.Fuzz.AccessViolationException827 + }; + private static readonly Dictionary CustomToleranceValues = new Dictionary { diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index e28caf6277..c99fe8d652 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -172,8 +172,31 @@ namespace SixLabors.ImageSharp.Tests public const string OrderedInterleavedProgressive723C = "Jpg/issues/Issue723-Ordered-Interleaved-Progressive-C.jpg"; public const string ExifGetString750Transform = "Jpg/issues/issue750-exif-tranform.jpg"; public const string ExifGetString750Load = "Jpg/issues/issue750-exif-load.jpg"; - public const string InvalidJpegThrowsWrongException797 = "Jpg/issues/Issue797-InvalidImage.jpg"; - public const string AccessViolationException798 = "Jpg/issues/Issue798-AccessViolationException.jpg"; + + public static class Fuzz + { + public const string NullReferenceException797 = "Jpg/issues/fuzz/Issue797-NullReferenceException.jpg"; + public const string AccessViolationException798 = "Jpg/issues/fuzz/Issue798-AccessViolationException.jpg"; + public const string DivideByZeroException821 = "Jpg/issues/fuzz/Issue821-DivideByZeroException.jpg"; + public const string DivideByZeroException822 = "Jpg/issues/fuzz/Issue822-DivideByZeroException.jpg"; + public const string NullReferenceException823 = "Jpg/issues/fuzz/Issue823-NullReferenceException.jpg"; + public const string IndexOutOfRangeException824A = "Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-A.jpg"; + public const string IndexOutOfRangeException824B = "Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-B.jpg"; + public const string IndexOutOfRangeException824C = "Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-C.jpg"; + public const string IndexOutOfRangeException824D = "Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-D.jpg"; + public const string IndexOutOfRangeException824E = "Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-E.jpg"; + public const string IndexOutOfRangeException824F = "Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-F.jpg"; + public const string IndexOutOfRangeException824G = "Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-G.jpg"; + public const string IndexOutOfRangeException824H = "Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-H.jpg"; + public const string ArgumentOutOfRangeException825A = "Jpg/issues/fuzz/Issue825-ArgumentOutOfRangeException-A.jpg"; + public const string ArgumentOutOfRangeException825B = "Jpg/issues/fuzz/Issue825-ArgumentOutOfRangeException-B.jpg"; + public const string ArgumentOutOfRangeException825C = "Jpg/issues/fuzz/Issue825-ArgumentOutOfRangeException-C.jpg"; + public const string ArgumentOutOfRangeException825D = "Jpg/issues/fuzz/Issue825-ArgumentOutOfRangeException-D.jpg"; + public const string ArgumentException826A = "Jpg/issues/fuzz/Issue826-ArgumentException-A.jpg"; + public const string ArgumentException826B = "Jpg/issues/fuzz/Issue826-ArgumentException-B.jpg"; + public const string ArgumentException826C = "Jpg/issues/fuzz/Issue826-ArgumentException-C.jpg"; + public const string AccessViolationException827 = "Jpg/issues/fuzz/Issue827-AccessViolationException.jpg"; + } } public static readonly string[] All = Baseline.All.Concat(Progressive.All).ToArray(); @@ -236,7 +259,7 @@ namespace SixLabors.ImageSharp.Tests public const string Rgba321010102 = "Bmp/rgba32-1010102.bmp"; public const string RgbaAlphaBitfields = "Bmp/rgba32abf.bmp"; - public static readonly string[] BitFields + public static readonly string[] BitFields = { Rgb32bfdef, Rgb32bf, diff --git a/tests/Images/External b/tests/Images/External index 32dc8aec11..c4098e463a 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 32dc8aec1109b681f056264e6bde1f3a76f08aba +Subproject commit c4098e463ab0e7128ae196b7f963369271df8fd3 diff --git a/tests/Images/Input/Jpg/issues/Issue214-CriticalEOF .jpg b/tests/Images/Input/Jpg/issues/Issue214-CriticalEOF.jpg similarity index 100% rename from tests/Images/Input/Jpg/issues/Issue214-CriticalEOF .jpg rename to tests/Images/Input/Jpg/issues/Issue214-CriticalEOF.jpg diff --git a/tests/Images/Input/Jpg/issues/Issue797-InvalidImage.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue797-NullReferenceException.jpg similarity index 100% rename from tests/Images/Input/Jpg/issues/Issue797-InvalidImage.jpg rename to tests/Images/Input/Jpg/issues/fuzz/Issue797-NullReferenceException.jpg diff --git a/tests/Images/Input/Jpg/issues/Issue798-AccessViolationException.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue798-AccessViolationException.jpg similarity index 100% rename from tests/Images/Input/Jpg/issues/Issue798-AccessViolationException.jpg rename to tests/Images/Input/Jpg/issues/fuzz/Issue798-AccessViolationException.jpg diff --git a/tests/Images/Input/Jpg/issues/fuzz/Issue821-DivideByZeroException.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue821-DivideByZeroException.jpg new file mode 100644 index 0000000000..23e95f1238 --- /dev/null +++ b/tests/Images/Input/Jpg/issues/fuzz/Issue821-DivideByZeroException.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:44f42f4b5c0491b7e8f5fe4b6a580c99bc5246cfe95238c601c75a7c5926e4b1 +size 397 diff --git a/tests/Images/Input/Jpg/issues/fuzz/Issue822-DivideByZeroException.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue822-DivideByZeroException.jpg new file mode 100644 index 0000000000..8a6fd02913 --- /dev/null +++ b/tests/Images/Input/Jpg/issues/fuzz/Issue822-DivideByZeroException.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:15107f755765118c8a1afac4a58f2945b3bd503ddabcc5b831219d1a159209d9 +size 397 diff --git a/tests/Images/Input/Jpg/issues/fuzz/Issue823-NullReferenceException.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue823-NullReferenceException.jpg new file mode 100644 index 0000000000..a404664aeb --- /dev/null +++ b/tests/Images/Input/Jpg/issues/fuzz/Issue823-NullReferenceException.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0aa96e68b91b56c4a6536e421e9a87a8388d1f205c3e1c3f7fd8f954a547fb26 +size 397 diff --git a/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-A.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-A.jpg new file mode 100644 index 0000000000..7df0579b03 --- /dev/null +++ b/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-A.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:19f76ba0a41511f384118214395f6f0911909b2e2304b7b2caf464e512c59006 +size 397 diff --git a/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-B.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-B.jpg new file mode 100644 index 0000000000..396cddb599 --- /dev/null +++ b/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-B.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9b75a87a3beb879c04bfac9ff5b2f11d939ac579d4d5e04fcc912ac95d38101e +size 397 diff --git a/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-C.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-C.jpg new file mode 100644 index 0000000000..4bd164ddd6 --- /dev/null +++ b/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-C.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0ee6b88dad441210f443cad24c6edba460fcc26404fcbbf0c134f9ccb3e92b0d +size 397 diff --git a/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-D.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-D.jpg new file mode 100644 index 0000000000..c80d7ba050 --- /dev/null +++ b/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-D.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3d17827181a497a5e0ca78d41347385f329eaa7fe25e8adb7cd7842ae1a656c8 +size 397 diff --git a/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-E.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-E.jpg new file mode 100644 index 0000000000..8d01b7427e --- /dev/null +++ b/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-E.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:617a0b15d8ddd07cd8c1d9a2e9fb1d09fa1b4756db7b91c5fbe1de233787ea1a +size 397 diff --git a/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-F.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-F.jpg new file mode 100644 index 0000000000..6430317995 --- /dev/null +++ b/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-F.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4834f4b8274e32ed44872a5365dd3cedd219b58538af592abf1692698afb3a95 +size 411 diff --git a/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-G.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-G.jpg new file mode 100644 index 0000000000..cc686eee0d --- /dev/null +++ b/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-G.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b0d3d7230934adb7f44875cfba7ce1688a10093e322712dc7fb2f2f07dfb93c6 +size 308 diff --git a/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-H.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-H.jpg new file mode 100644 index 0000000000..132c9043f7 --- /dev/null +++ b/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-H.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1478c189574a9c5f9492ae1f9ea2093a74255299d0f293d7fe9c41a76be381e3 +size 308 diff --git a/tests/Images/Input/Jpg/issues/fuzz/Issue825-ArgumentOutOfRangeException-A.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue825-ArgumentOutOfRangeException-A.jpg new file mode 100644 index 0000000000..845a526350 --- /dev/null +++ b/tests/Images/Input/Jpg/issues/fuzz/Issue825-ArgumentOutOfRangeException-A.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c095034c82addf710d5d9127447b894bb052642800c40b803517547c0b316054 +size 397 diff --git a/tests/Images/Input/Jpg/issues/fuzz/Issue825-ArgumentOutOfRangeException-B.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue825-ArgumentOutOfRangeException-B.jpg new file mode 100644 index 0000000000..57dd33e234 --- /dev/null +++ b/tests/Images/Input/Jpg/issues/fuzz/Issue825-ArgumentOutOfRangeException-B.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b12df8907f0a9a0a89ac24ff73db43d8e85d47558a20cb186764cc0533bdb214 +size 397 diff --git a/tests/Images/Input/Jpg/issues/fuzz/Issue825-ArgumentOutOfRangeException-C.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue825-ArgumentOutOfRangeException-C.jpg new file mode 100644 index 0000000000..2e595e36ae --- /dev/null +++ b/tests/Images/Input/Jpg/issues/fuzz/Issue825-ArgumentOutOfRangeException-C.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:78ca8307aaacd980939be0002210dacbe09e133a8249f0d671e89f45b229abc0 +size 397 diff --git a/tests/Images/Input/Jpg/issues/fuzz/Issue825-ArgumentOutOfRangeException-D.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue825-ArgumentOutOfRangeException-D.jpg new file mode 100644 index 0000000000..7c3db77d85 --- /dev/null +++ b/tests/Images/Input/Jpg/issues/fuzz/Issue825-ArgumentOutOfRangeException-D.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c6dba5ec29565cbc9a70b0ea70fe03409d73cfef6af9a8519b579bc4947252bf +size 380 diff --git a/tests/Images/Input/Jpg/issues/fuzz/Issue826-ArgumentException-A.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue826-ArgumentException-A.jpg new file mode 100644 index 0000000000..5766e13d53 --- /dev/null +++ b/tests/Images/Input/Jpg/issues/fuzz/Issue826-ArgumentException-A.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dfd61ccf7df77b9eff15bca98fad4df0eef095c8c8ca690344fc459d9706a50c +size 397 diff --git a/tests/Images/Input/Jpg/issues/fuzz/Issue826-ArgumentException-B.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue826-ArgumentException-B.jpg new file mode 100644 index 0000000000..625ecc61d4 --- /dev/null +++ b/tests/Images/Input/Jpg/issues/fuzz/Issue826-ArgumentException-B.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:60a198357053526cbfc0fd9fc90d8690cdc72b9b75e88a926d6e7b7664a89343 +size 397 diff --git a/tests/Images/Input/Jpg/issues/fuzz/Issue826-ArgumentException-C.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue826-ArgumentException-C.jpg new file mode 100644 index 0000000000..fa11080385 --- /dev/null +++ b/tests/Images/Input/Jpg/issues/fuzz/Issue826-ArgumentException-C.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:38c6415405ac1303d359a0327a3b2517e7bad99df9fd3c0f4cbae979be56f0d7 +size 409 diff --git a/tests/Images/Input/Jpg/issues/fuzz/Issue827-AccessViolationException.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue827-AccessViolationException.jpg new file mode 100644 index 0000000000..177bb082a9 --- /dev/null +++ b/tests/Images/Input/Jpg/issues/fuzz/Issue827-AccessViolationException.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0e7282c0dbea030f2e18eef12685dc17fa37b03e0f67cf71d8917106d470b94f +size 397 From a69eb5bbedf01225aa4e105c28342f484ba13d40 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 23 Feb 2019 21:36:37 +1100 Subject: [PATCH 025/223] Lazily derive the huffman tables. Fix #839 (#841) * Lazily derive the huffman tables. Fix #839 * Lazy invoke fast table * Add performance tweaks to scan decoder. * Remove unneccessary classes. --- .../Jpeg/Components/Decoder/FastACTable.cs | 61 +++++++ .../Jpeg/Components/Decoder/FastACTables.cs | 90 ---------- .../Jpeg/Components/Decoder/HuffmanTable.cs | 31 +++- .../Jpeg/Components/Decoder/HuffmanTables.cs | 26 --- .../Jpeg/Components/Decoder/ScanDecoder.cs | 164 ++++++++++++------ .../Formats/Jpeg/JpegDecoderCore.cs | 22 +-- .../Formats/Jpg/JpegDecoderTests.Images.cs | 3 +- tests/ImageSharp.Tests/TestImages.cs | 1 + .../Issue839-ExecutionEngineException.jpg | 3 + 9 files changed, 212 insertions(+), 189 deletions(-) create mode 100644 src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTable.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTables.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTables.cs create mode 100644 tests/Images/Input/Jpg/issues/fuzz/Issue839-ExecutionEngineException.jpg diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTable.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTable.cs new file mode 100644 index 0000000000..57d2221ba3 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTable.cs @@ -0,0 +1,61 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder +{ + internal unsafe struct FastACTable + { + /// + /// Gets the lookahead array + /// + public fixed short Lookahead[512]; + + /// + /// Derives a lookup table for fast AC entropy scan decoding. + /// This can happen multiple times during progressive decoding but always outside mcu loops. + /// + /// The AC Huffman table. + public void Derive(ref HuffmanTable huffmanTable) + { + const int FastBits = ScanDecoder.FastBits; + ref short fastACRef = ref this.Lookahead[0]; + ref byte huffmanLookaheadRef = ref huffmanTable.Lookahead[0]; + ref byte huffmanValuesRef = ref huffmanTable.Values[0]; + ref short huffmanSizesRef = ref huffmanTable.Sizes[0]; + + int i; + for (i = 0; i < (1 << FastBits); i++) + { + byte fast = Unsafe.Add(ref huffmanLookaheadRef, i); + Unsafe.Add(ref fastACRef, i) = 0; + + if (fast < byte.MaxValue) + { + int rs = Unsafe.Add(ref huffmanValuesRef, fast); + int run = (rs >> 4) & 15; + int magbits = rs & 15; + int len = Unsafe.Add(ref huffmanSizesRef, fast); + + if (magbits != 0 && len + magbits <= FastBits) + { + // Magnitude code followed by receive_extend code + int k = ((i << len) & ((1 << FastBits) - 1)) >> (FastBits - magbits); + int m = 1 << (magbits - 1); + if (k < m) + { + k += (int)((~0U << magbits) + 1); + } + + // If the result is small enough, we can fit it in fastAC table + if (k >= -128 && k <= 127) + { + Unsafe.Add(ref fastACRef, i) = (short)((k << 8) + (run << 4) + (len + magbits)); + } + } + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTables.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTables.cs deleted file mode 100644 index 06b46746a6..0000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTables.cs +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Runtime.CompilerServices; - -using SixLabors.ImageSharp.Memory; -using SixLabors.Memory; - -namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder -{ - /// - /// The collection of lookup tables used for fast AC entropy scan decoding. - /// - internal sealed class FastACTables : IDisposable - { - private Buffer2D tables; - - /// - /// Initializes a new instance of the class. - /// - /// The memory allocator used to allocate memory for image processing operations. - public FastACTables(MemoryAllocator memoryAllocator) => this.tables = memoryAllocator.Allocate2D(512, 4, AllocationOptions.Clean); - - /// - /// Gets the representing the table at the index in the collection. - /// - /// The table index. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public ReadOnlySpan GetTableSpan(int index) => this.tables.GetRowSpan(index); - - /// - /// Gets a reference to the first element of the AC table indexed by - /// - /// The frame component. - [MethodImpl(InliningOptions.ShortMethod)] - public ref short GetAcTableReference(JpegComponent component) => ref this.tables.GetRowSpan(component.ACHuffmanTableId)[0]; - - /// - /// Builds a lookup table for fast AC entropy scan decoding. - /// - /// The table index. - /// The collection of AC Huffman tables. - public unsafe void BuildACTableLut(int index, HuffmanTables acHuffmanTables) - { - const int FastBits = ScanDecoder.FastBits; - Span fastAC = this.tables.GetRowSpan(index); - ref HuffmanTable huffmanTable = ref acHuffmanTables[index]; - - int i; - for (i = 0; i < (1 << FastBits); i++) - { - byte fast = huffmanTable.Lookahead[i]; - fastAC[i] = 0; - if (fast < byte.MaxValue) - { - int rs = huffmanTable.Values[fast]; - int run = (rs >> 4) & 15; - int magbits = rs & 15; - int len = huffmanTable.Sizes[fast]; - - if (magbits != 0 && len + magbits <= FastBits) - { - // Magnitude code followed by receive_extend code - int k = ((i << len) & ((1 << FastBits) - 1)) >> (FastBits - magbits); - int m = 1 << (magbits - 1); - if (k < m) - { - k += (int)((~0U << magbits) + 1); - } - - // if the result is small enough, we can fit it in fastAC table - if (k >= -128 && k <= 127) - { - fastAC[i] = (short)((k << 8) + (run << 4) + (len + magbits)); - } - } - } - } - } - - /// - public void Dispose() - { - this.tables?.Dispose(); - this.tables = null; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs index 90b3624651..a2c9bade19 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs @@ -17,6 +17,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder [StructLayout(LayoutKind.Sequential)] internal unsafe struct HuffmanTable { + private bool isDerived; + + private readonly MemoryAllocator memoryAllocator; + +#pragma warning disable IDE0044 // Add readonly modifier + private fixed byte codeLengths[17]; +#pragma warning restore IDE0044 // Add readonly modifier + /// /// Gets the max code array /// @@ -50,16 +58,31 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// The huffman values public HuffmanTable(MemoryAllocator memoryAllocator, ReadOnlySpan codeLengths, ReadOnlySpan values) { + this.isDerived = false; + this.memoryAllocator = memoryAllocator; + Unsafe.CopyBlockUnaligned(ref this.codeLengths[0], ref MemoryMarshal.GetReference(codeLengths), (uint)codeLengths.Length); + Unsafe.CopyBlockUnaligned(ref this.Values[0], ref MemoryMarshal.GetReference(values), (uint)values.Length); + } + + /// + /// Expands the HuffmanTable into its derived form. + /// + public void Derive() + { + if (this.isDerived) + { + return; + } + const int Length = 257; - using (IMemoryOwner huffcode = memoryAllocator.Allocate(Length)) + using (IMemoryOwner huffcode = this.memoryAllocator.Allocate(Length)) { ref short huffcodeRef = ref MemoryMarshal.GetReference(huffcode.GetSpan()); - ref byte codeLengthsRef = ref MemoryMarshal.GetReference(codeLengths); + ref byte codeLengthsRef = ref this.codeLengths[0]; // Figure C.1: make table of Huffman code length for each symbol ref short sizesRef = ref this.Sizes[0]; short x = 0; - for (short i = 1; i < 17; i++) { byte length = Unsafe.Add(ref codeLengthsRef, i); @@ -119,7 +142,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder } } - Unsafe.CopyBlockUnaligned(ref this.Values[0], ref MemoryMarshal.GetReference(values), 256); + this.isDerived = true; } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTables.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTables.cs deleted file mode 100644 index dc066aa0ac..0000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTables.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder -{ - /// - /// Defines a 2 pairs of huffman tables. - /// - internal sealed class HuffmanTables - { - private readonly HuffmanTable[] tables = new HuffmanTable[4]; - - /// - /// Gets or sets the table at the given index. - /// - /// The index - /// The - public ref HuffmanTable this[int index] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => ref this.tables[index]; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs index 070652a9f9..ef4b359ff2 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs @@ -3,6 +3,7 @@ using System; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; @@ -25,9 +26,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder private static readonly int[] Bias = { 0, -1, -3, -7, -15, -31, -63, -127, -255, -511, -1023, -2047, -4095, -8191, -16383, -32767 }; private readonly JpegFrame frame; - private readonly HuffmanTables dcHuffmanTables; - private readonly HuffmanTables acHuffmanTables; - private readonly FastACTables fastACTables; + private readonly HuffmanTable[] dcHuffmanTables; + private readonly HuffmanTable[] acHuffmanTables; + private readonly FastACTable[] fastACTables; private readonly DoubleBufferedStreamReader stream; private readonly JpegComponent[] components; @@ -95,9 +96,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder public ScanDecoder( DoubleBufferedStreamReader stream, JpegFrame frame, - HuffmanTables dcHuffmanTables, - HuffmanTables acHuffmanTables, - FastACTables fastACTables, + HuffmanTable[] dcHuffmanTables, + HuffmanTable[] acHuffmanTables, + FastACTable[] fastACTables, int componentsLength, int restartInterval, int spectralStart, @@ -159,17 +160,35 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder } } - private void ParseBaselineDataInterleaved() + private unsafe void ParseBaselineDataInterleaved() { // Interleaved int mcu = 0; int mcusPerColumn = this.frame.McusPerColumn; int mcusPerLine = this.frame.McusPerLine; + + // Pre-derive the huffman table to avoid in-loop checks. + for (int i = 0; i < this.componentsLength; i++) + { + int order = this.frame.ComponentOrder[i]; + JpegComponent component = this.components[order]; + + ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; + ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId]; + dcHuffmanTable.Derive(); + acHuffmanTable.Derive(); + + ref FastACTable fastAcTable = ref this.fastACTables[component.ACHuffmanTableId]; + fastAcTable.Derive(ref acHuffmanTable); + } + for (int j = 0; j < mcusPerColumn; j++) { for (int i = 0; i < mcusPerLine; i++) { // Scan an interleaved mcu... process components in order + int mcuRow = mcu / mcusPerLine; + int mcuCol = mcu % mcusPerLine; for (int k = 0; k < this.componentsLength; k++) { int order = this.frame.ComponentOrder[k]; @@ -177,18 +196,20 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId]; - ref short fastACRef = ref this.fastACTables.GetAcTableReference(component); + ref FastACTable fastAcTable = ref this.fastACTables[component.ACHuffmanTableId]; + ref short fastACRef = ref fastAcTable.Lookahead[0]; + int h = component.HorizontalSamplingFactor; int v = component.VerticalSamplingFactor; - int mcuRow = mcu / mcusPerLine; - // Scan out an mcu's worth of this component; that's just determined // by the basic H and V specified for the component for (int y = 0; y < v; y++) { int blockRow = (mcuRow * v) + y; Span blockSpan = component.SpectralBlocks.GetRowSpan(blockRow); + ref Block8x8 blockRef = ref MemoryMarshal.GetReference(blockSpan); + for (int x = 0; x < h; x++) { if (this.eof) @@ -196,12 +217,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder return; } - int mcuCol = mcu % mcusPerLine; int blockCol = (mcuCol * h) + x; this.DecodeBlockBaseline( component, - ref blockSpan[blockCol], + ref Unsafe.Add(ref blockRef, blockCol), ref dcHuffmanTable, ref acHuffmanTable, ref fastACRef); @@ -225,7 +245,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// number of blocks to do just depends on how many actual "pixels" each component has, /// independent of interleaved MCU blocking and such. /// - private void ParseBaselineDataNonInterleaved() + private unsafe void ParseBaselineDataNonInterleaved() { JpegComponent component = this.components[this.frame.ComponentOrder[0]]; @@ -234,14 +254,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId]; - ref short fastACRef = ref this.fastACTables.GetAcTableReference(component); + dcHuffmanTable.Derive(); + acHuffmanTable.Derive(); + + ref FastACTable fastAcTable = ref this.fastACTables[component.ACHuffmanTableId]; + fastAcTable.Derive(ref acHuffmanTable); + ref short fastACRef = ref fastAcTable.Lookahead[0]; int mcu = 0; for (int j = 0; j < h; j++) { - // TODO: Isn't blockRow == j actually? - int blockRow = mcu / w; - Span blockSpan = component.SpectralBlocks.GetRowSpan(blockRow); + int blockRow = j; + Span blockSpan = component.SpectralBlocks.GetRowSpan(j); + ref Block8x8 blockRef = ref MemoryMarshal.GetReference(blockSpan); for (int i = 0; i < w; i++) { @@ -250,12 +275,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder return; } - // TODO: Isn't blockCol == i actually? - int blockCol = mcu % w; - this.DecodeBlockBaseline( component, - ref blockSpan[blockCol], + ref Unsafe.Add(ref blockRef, i), ref dcHuffmanTable, ref acHuffmanTable, ref fastACRef); @@ -340,16 +362,29 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder int mcu = 0; int mcusPerColumn = this.frame.McusPerColumn; int mcusPerLine = this.frame.McusPerLine; + + // Pre-derive the huffman table to avoid in-loop checks. + for (int k = 0; k < this.componentsLength; k++) + { + int order = this.frame.ComponentOrder[k]; + JpegComponent component = this.components[order]; + ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; + dcHuffmanTable.Derive(); + } + for (int j = 0; j < mcusPerColumn; j++) { for (int i = 0; i < mcusPerLine; i++) { // Scan an interleaved mcu... process components in order + int mcuRow = mcu / mcusPerLine; + int mcuCol = mcu % mcusPerLine; for (int k = 0; k < this.componentsLength; k++) { int order = this.frame.ComponentOrder[k]; JpegComponent component = this.components[order]; ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; + int h = component.HorizontalSamplingFactor; int v = component.VerticalSamplingFactor; @@ -357,9 +392,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder // by the basic H and V specified for the component for (int y = 0; y < v; y++) { - int mcuRow = mcu / mcusPerLine; int blockRow = (mcuRow * v) + y; Span blockSpan = component.SpectralBlocks.GetRowSpan(blockRow); + ref Block8x8 blockRef = ref MemoryMarshal.GetReference(blockSpan); for (int x = 0; x < h; x++) { @@ -368,12 +403,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder return; } - int mcuCol = mcu % mcusPerLine; int blockCol = (mcuCol * h) + x; this.DecodeBlockProgressiveDC( component, - ref blockSpan[blockCol], + ref Unsafe.Add(ref blockRef, blockCol), ref dcHuffmanTable); } } @@ -396,56 +430,78 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// number of blocks to do just depends on how many actual "pixels" this /// component has, independent of interleaved MCU blocking and such ///
- private void ParseProgressiveDataNonInterleaved() + private unsafe void ParseProgressiveDataNonInterleaved() { JpegComponent component = this.components[this.frame.ComponentOrder[0]]; int w = component.WidthInBlocks; int h = component.HeightInBlocks; - ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; - ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId]; - ref short fastACRef = ref this.fastACTables.GetAcTableReference(component); - - int mcu = 0; - for (int j = 0; j < h; j++) + if (this.spectralStart == 0) { - // TODO: isn't blockRow == j actually? - int blockRow = mcu / w; - Span blockSpan = component.SpectralBlocks.GetRowSpan(blockRow); + ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; + dcHuffmanTable.Derive(); - for (int i = 0; i < w; i++) + int mcu = 0; + for (int j = 0; j < h; j++) { - if (this.eof) - { - return; - } - - // TODO: isn't blockCol == i actually? - int blockCol = mcu % w; + Span blockSpan = component.SpectralBlocks.GetRowSpan(j); + ref Block8x8 blockRef = ref MemoryMarshal.GetReference(blockSpan); - ref Block8x8 block = ref blockSpan[blockCol]; - - if (this.spectralStart == 0) + for (int i = 0; i < w; i++) { + if (this.eof) + { + return; + } + this.DecodeBlockProgressiveDC( component, - ref block, + ref Unsafe.Add(ref blockRef, i), ref dcHuffmanTable); + + // Every data block is an MCU, so countdown the restart interval + mcu++; + if (!this.ContinueOnMcuComplete()) + { + return; + } } - else + } + } + else + { + ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId]; + acHuffmanTable.Derive(); + + ref FastACTable fastAcTable = ref this.fastACTables[component.ACHuffmanTableId]; + fastAcTable.Derive(ref acHuffmanTable); + ref short fastACRef = ref fastAcTable.Lookahead[0]; + + int mcu = 0; + for (int j = 0; j < h; j++) + { + Span blockSpan = component.SpectralBlocks.GetRowSpan(j); + ref Block8x8 blockRef = ref MemoryMarshal.GetReference(blockSpan); + + for (int i = 0; i < w; i++) { + if (this.eof) + { + return; + } + this.DecodeBlockProgressiveAC( - ref block, + ref Unsafe.Add(ref blockRef, i), ref acHuffmanTable, ref fastACRef); - } - // Every data block is an MCU, so countdown the restart interval - mcu++; - if (!this.ContinueOnMcuComplete()) - { - return; + // Every data block is an MCU, so countdown the restart interval + mcu++; + if (!this.ContinueOnMcuComplete()) + { + return; + } } } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 9bcfd9ff64..891dd52826 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -52,17 +52,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// /// The DC Huffman tables /// - private HuffmanTables dcHuffmanTables; + private HuffmanTable[] dcHuffmanTables; /// /// The AC Huffman tables /// - private HuffmanTables acHuffmanTables; + private HuffmanTable[] acHuffmanTables; /// /// The fast AC tables used for entropy decoding /// - private FastACTables fastACTables; + private FastACTable[] fastACTables; /// /// The reset interval determined by RST markers @@ -266,9 +266,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg // Only assign what we need if (!metadataOnly) { - this.dcHuffmanTables = new HuffmanTables(); - this.acHuffmanTables = new HuffmanTables(); - this.fastACTables = new FastACTables(this.configuration.MemoryAllocator); + const int maxTables = 4; + this.dcHuffmanTables = new HuffmanTable[maxTables]; + this.acHuffmanTables = new HuffmanTable[maxTables]; + this.fastACTables = new FastACTable[maxTables]; } // Break only when we discover a valid EOI marker. @@ -378,7 +379,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg { this.InputStream?.Dispose(); this.Frame?.Dispose(); - this.fastACTables?.Dispose(); // Set large fields to null. this.InputStream = null; @@ -872,12 +872,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg tableIndex, codeLengths.GetSpan(), huffmanValues.GetSpan()); - - if (tableType != 0) - { - // Build a table that decodes both magnitude and value of small ACs in one go. - this.fastACTables.BuildACTableLut(tableIndex, this.acHuffmanTables); - } } } } @@ -967,7 +961,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// The codelengths /// The values [MethodImpl(InliningOptions.ShortMethod)] - private void BuildHuffmanTable(HuffmanTables tables, int index, ReadOnlySpan codeLengths, ReadOnlySpan values) + private void BuildHuffmanTable(HuffmanTable[] tables, int index, ReadOnlySpan codeLengths, ReadOnlySpan values) => tables[index] = new HuffmanTable(this.configuration.MemoryAllocator, codeLengths, values); /// diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs index ff49de2481..8a94826077 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs @@ -79,7 +79,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg TestImages.Jpeg.Issues.Fuzz.ArgumentException826A, TestImages.Jpeg.Issues.Fuzz.ArgumentException826B, TestImages.Jpeg.Issues.Fuzz.ArgumentException826C, - TestImages.Jpeg.Issues.Fuzz.AccessViolationException827 + TestImages.Jpeg.Issues.Fuzz.AccessViolationException827, + TestImages.Jpeg.Issues.Fuzz.ExecutionEngineException839 }; private static readonly Dictionary CustomToleranceValues = diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index c99fe8d652..cf1e49b271 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -196,6 +196,7 @@ namespace SixLabors.ImageSharp.Tests public const string ArgumentException826B = "Jpg/issues/fuzz/Issue826-ArgumentException-B.jpg"; public const string ArgumentException826C = "Jpg/issues/fuzz/Issue826-ArgumentException-C.jpg"; public const string AccessViolationException827 = "Jpg/issues/fuzz/Issue827-AccessViolationException.jpg"; + public const string ExecutionEngineException839 = "Jpg/issues/fuzz/Issue839-ExecutionEngineException.jpg"; } } diff --git a/tests/Images/Input/Jpg/issues/fuzz/Issue839-ExecutionEngineException.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue839-ExecutionEngineException.jpg new file mode 100644 index 0000000000..b411b970a7 --- /dev/null +++ b/tests/Images/Input/Jpg/issues/fuzz/Issue839-ExecutionEngineException.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b37ac1cc34680db9721542ef5354f7509860afd37888a24f6350f29f5b63eea1 +size 397 From 794fe208517883e983f4b8b3d355ad64846c64d1 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 16 Mar 2019 10:16:17 +1100 Subject: [PATCH 026/223] Implement IEquatable and check when summing. (#848) --- .../Formats/Jpeg/Components/Block8x8F.cs | 23 ++++++++++++++++++- .../Components/Decoder/QualityEvaluator.cs | 8 +++++-- .../Formats/Jpg/JpegDecoderTests.MetaData.cs | 5 ++-- tests/ImageSharp.Tests/TestImages.cs | 1 + .../issues/Issue845-Incorrect-Quality99.jpg | 3 +++ 5 files changed, 35 insertions(+), 5 deletions(-) create mode 100644 tests/Images/Input/Jpg/issues/Issue845-Incorrect-Quality99.jpg diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs index c9c886f057..c0de450695 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components /// /// Represents a Jpeg block with coefficients. /// - internal partial struct Block8x8F + internal partial struct Block8x8F : IEquatable { /// /// A number of scalar coefficients in a @@ -538,6 +538,27 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components Unsafe.Add(ref dRef, 7) = bottom; } + /// + public bool Equals(Block8x8F other) + { + return this.V0L == other.V0L + && this.V0R == other.V0R + && this.V1L == other.V1L + && this.V1R == other.V1R + && this.V2L == other.V2L + && this.V2R == other.V2R + && this.V3L == other.V3L + && this.V3R == other.V3R + && this.V4L == other.V4L + && this.V4R == other.V4R + && this.V5L == other.V5L + && this.V5R == other.V5R + && this.V6L == other.V6L + && this.V6R == other.V6R + && this.V7L == other.V7L + && this.V7R == other.V7R; + } + /// public override string ToString() { diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/QualityEvaluator.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/QualityEvaluator.cs index 4e11568c8d..a7d2a0fde9 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/QualityEvaluator.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/QualityEvaluator.cs @@ -84,9 +84,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder for (int i = 0; i < quantizationTables.Length; i++) { ref Block8x8F qTable = ref quantizationTables[i]; - for (int j = 0; j < Block8x8F.Size; j++) + + if (!qTable.Equals(default)) { - sum += qTable[j]; + for (int j = 0; j < Block8x8F.Size; j++) + { + sum += qTable[j]; + } } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.MetaData.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.MetaData.cs index 4810985f11..1f1583a39f 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.MetaData.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.MetaData.cs @@ -52,8 +52,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg public static readonly TheoryData QualityFiles = new TheoryData { - { TestImages.Jpeg.Baseline.Calliphora, 80}, - { TestImages.Jpeg.Progressive.Fb, 75 } + { TestImages.Jpeg.Baseline.Calliphora, 80 }, + { TestImages.Jpeg.Progressive.Fb, 75 }, + { TestImages.Jpeg.Issues.IncorrectQuality845, 99 } }; [Theory] diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index cf1e49b271..3f12fab125 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -172,6 +172,7 @@ namespace SixLabors.ImageSharp.Tests public const string OrderedInterleavedProgressive723C = "Jpg/issues/Issue723-Ordered-Interleaved-Progressive-C.jpg"; public const string ExifGetString750Transform = "Jpg/issues/issue750-exif-tranform.jpg"; public const string ExifGetString750Load = "Jpg/issues/issue750-exif-load.jpg"; + public const string IncorrectQuality845 = "Jpg/issues/Issue845-Incorrect-Quality99.jpg"; public static class Fuzz { diff --git a/tests/Images/Input/Jpg/issues/Issue845-Incorrect-Quality99.jpg b/tests/Images/Input/Jpg/issues/Issue845-Incorrect-Quality99.jpg new file mode 100644 index 0000000000..639fce534e --- /dev/null +++ b/tests/Images/Input/Jpg/issues/Issue845-Incorrect-Quality99.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4917339488d46f25ef9ffba6bcfe42272fb190808da7c24e7a3d3f31e0b68a8c +size 2270501 From 512647489f47ca0bbc39b3fb2f068a5974fccd9c Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 21 Mar 2019 10:30:38 -0700 Subject: [PATCH 027/223] Bounds check. Fix #849 (#851) --- src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs | 9 ++++++++- tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs | 3 ++- tests/ImageSharp.Tests/TestImages.cs | 1 + tests/Images/Input/Png/zlib-overflow.png | 3 +++ 4 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 tests/Images/Input/Png/zlib-overflow.png diff --git a/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs b/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs index 583175b566..71141a8939 100644 --- a/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs +++ b/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs @@ -126,8 +126,9 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib int bytesToRead = Math.Min(count, this.currentDataRemaining); this.currentDataRemaining -= bytesToRead; int bytesRead = this.innerStream.Read(buffer, offset, bytesToRead); + long length = this.innerStream.Length; - // keep reading data until we've reached the end of the stream or filled the buffer + // Keep reading data until we've reached the end of the stream or filled the buffer while (this.currentDataRemaining == 0 && bytesRead < count) { this.currentDataRemaining = this.getData(); @@ -138,6 +139,12 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib } offset += bytesRead; + + if (offset >= length) + { + return bytesRead; + } + bytesToRead = Math.Min(count - bytesRead, this.currentDataRemaining); this.currentDataRemaining -= bytesToRead; bytesRead += this.innerStream.Read(buffer, offset, bytesToRead); diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index eca7a2543a..fe406c9924 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -41,7 +41,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png TestImages.Png.Rgb24BppTrans, TestImages.Png.GrayAlpha8Bit, - TestImages.Png.Gray1BitTrans + TestImages.Png.Gray1BitTrans, + TestImages.Png.Bad.ZlibOverflow }; public static readonly string[] TestImages48Bpp = diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 3f12fab125..9099696247 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -85,6 +85,7 @@ namespace SixLabors.ImageSharp.Tests public const string ChunkLength1 = "Png/chunklength1.png"; public const string ChunkLength2 = "Png/chunklength2.png"; public const string CorruptedChunk = "Png/big-corrupted-chunk.png"; + public const string ZlibOverflow = "Png/zlib-overflow.png"; } public static readonly string[] All = diff --git a/tests/Images/Input/Png/zlib-overflow.png b/tests/Images/Input/Png/zlib-overflow.png new file mode 100644 index 0000000000..07bf660b8a --- /dev/null +++ b/tests/Images/Input/Png/zlib-overflow.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2705125a7b108c7ef4e13872be88b991cd06ba97d81a306f70f58749cec53514 +size 10725 From 701d6124b3f6614e1b741f84db90a5e7771eff14 Mon Sep 17 00:00:00 2001 From: Kjetil Klaussen Date: Thu, 21 Mar 2019 18:43:38 +0100 Subject: [PATCH 028/223] #852 Adding instructions to add submodules when cloning the repository --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index a72074f8f3..079f2106f9 100644 --- a/README.md +++ b/README.md @@ -123,6 +123,14 @@ To clone ImageSharp locally click the "Clone in Windows" button above or run the git clone https://github.com/SixLabors/ImageSharp ``` +### Submodules + +This repository contains [git submodule](https://blog.github.com/2016-02-01-working-with-submodules/). To add the submodules to the project, navigate to the repository root and type: + +``` bash +git submodule update --init --recursive +``` + ### How can you help? Please... Spread the word, contribute algorithms, submit performance improvements, unit tests, no input is too little. Make sure to read our [Contribution Guide](https://github.com/SixLabors/ImageSharp/blob/master/.github/CONTRIBUTING.md) before opening a PR. From 5a550c4f25d597cf464203d8d2bf59af7a8f6fc1 Mon Sep 17 00:00:00 2001 From: Kjetil Klaussen Date: Thu, 21 Mar 2019 18:48:23 +0100 Subject: [PATCH 029/223] fix: Typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 079f2106f9..ceb28564b9 100644 --- a/README.md +++ b/README.md @@ -125,7 +125,7 @@ git clone https://github.com/SixLabors/ImageSharp ### Submodules -This repository contains [git submodule](https://blog.github.com/2016-02-01-working-with-submodules/). To add the submodules to the project, navigate to the repository root and type: +This repository contains [git submodules](https://blog.github.com/2016-02-01-working-with-submodules/). To add the submodules to the project, navigate to the repository root and type: ``` bash git submodule update --init --recursive From bcc19cf20dd0c994a80b666a0de44bac51f66af0 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Mon, 25 Mar 2019 18:30:29 -0700 Subject: [PATCH 030/223] Updating some readonly static data in JpegEncoderCore to take advantage of compiler functionality. (#855) --- .../Common/Extensions/StreamExtensions.cs | 21 ++ .../Formats/Jpeg/JpegEncoderCore.cs | 185 ++++++++++-------- 2 files changed, 124 insertions(+), 82 deletions(-) diff --git a/src/ImageSharp/Common/Extensions/StreamExtensions.cs b/src/ImageSharp/Common/Extensions/StreamExtensions.cs index a200ffebcc..505ecccdda 100644 --- a/src/ImageSharp/Common/Extensions/StreamExtensions.cs +++ b/src/ImageSharp/Common/Extensions/StreamExtensions.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.IO; using SixLabors.ImageSharp.Memory; @@ -82,5 +83,25 @@ namespace SixLabors.ImageSharp { stream.Write(buffer.Array, 0, buffer.Length()); } + +#if NET472 || NETSTANDARD1_3 || NETSTANDARD2_0 + // This is a port of the CoreFX implementation and is MIT Licensed: https://github.com/dotnet/coreclr/blob/c4dca1072d15bdda64c754ad1ea474b1580fa554/src/System.Private.CoreLib/shared/System/IO/Stream.cs#L770 + public static void Write(this Stream stream, ReadOnlySpan buffer) + { + // This uses ArrayPool.Shared, rather than taking a MemoryAllocator, + // in order to match the signature of the framework method that exists in + // .NET Core. + byte[] sharedBuffer = ArrayPool.Shared.Rent(buffer.Length); + try + { + buffer.CopyTo(sharedBuffer); + stream.Write(sharedBuffer, 0, buffer.Length); + } + finally + { + ArrayPool.Shared.Return(sharedBuffer); + } + } +#endif } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs index b3f30f815f..4544244f08 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs @@ -27,85 +27,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// private const int QuantizationTableCount = 2; - /// - /// Counts the number of bits needed to hold an integer. - /// - private static readonly uint[] BitCountLut = - { - 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, - }; - - /// - /// The SOS (Start Of Scan) marker "\xff\xda" followed by 12 bytes: - /// - the marker length "\x00\x0c", - /// - the number of components "\x03", - /// - component 1 uses DC table 0 and AC table 0 "\x01\x00", - /// - component 2 uses DC table 1 and AC table 1 "\x02\x11", - /// - component 3 uses DC table 1 and AC table 1 "\x03\x11", - /// - the bytes "\x00\x3f\x00". Section B.2.3 of the spec says that for - /// sequential DCTs, those bytes (8-bit Ss, 8-bit Se, 4-bit Ah, 4-bit Al) - /// should be 0x00, 0x3f, 0x00<<4 | 0x00. - /// - private static readonly byte[] SosHeaderYCbCr = - { - JpegConstants.Markers.XFF, JpegConstants.Markers.SOS, - - // Marker - 0x00, 0x0c, - - // Length (high byte, low byte), must be 6 + 2 * (number of components in scan) - 0x03, // Number of components in a scan, 3 - 0x01, // Component Id Y - 0x00, // DC/AC Huffman table - 0x02, // Component Id Cb - 0x11, // DC/AC Huffman table - 0x03, // Component Id Cr - 0x11, // DC/AC Huffman table - 0x00, // Ss - Start of spectral selection. - 0x3f, // Se - End of spectral selection. - 0x00 - - // Ah + Ah (Successive approximation bit position high + low) - }; - - /// - /// The unscaled quantization tables in zig-zag order. Each - /// encoder copies and scales the tables according to its quality parameter. - /// The values are derived from section K.1 after converting from natural to - /// zig-zag order. - /// - private static readonly byte[,] UnscaledQuant = - { - { - // Luminance. - 16, 11, 12, 14, 12, 10, 16, 14, 13, 14, 18, 17, 16, 19, 24, - 40, 26, 24, 22, 22, 24, 49, 35, 37, 29, 40, 58, 51, 61, 60, - 57, 51, 56, 55, 64, 72, 92, 78, 64, 68, 87, 69, 55, 56, 80, - 109, 81, 87, 95, 98, 103, 104, 103, 62, 77, 113, 121, 112, - 100, 120, 92, 101, 103, 99, - }, - { - // Chrominance. - 17, 18, 18, 24, 21, 24, 47, 26, 26, 47, 99, 66, 56, 66, - 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - } - }; - /// /// A scratch buffer to reduce allocations. /// @@ -167,6 +88,103 @@ namespace SixLabors.ImageSharp.Formats.Jpeg this.subsample = options.Subsample; } + /// + /// Gets the counts the number of bits needed to hold an integer. + /// + // The C# compiler emits this as a compile-time constant embedded in the PE file. + // This is effectively compiled down to: return new ReadOnlySpan(&data, length) + // More details can be found: https://github.com/dotnet/roslyn/pull/24621 + private static ReadOnlySpan BitCountLut => new byte[] + { + 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, + }; + + /// + /// Gets the SOS (Start Of Scan) marker "\xff\xda" followed by 12 bytes: + /// - the marker length "\x00\x0c", + /// - the number of components "\x03", + /// - component 1 uses DC table 0 and AC table 0 "\x01\x00", + /// - component 2 uses DC table 1 and AC table 1 "\x02\x11", + /// - component 3 uses DC table 1 and AC table 1 "\x03\x11", + /// - the bytes "\x00\x3f\x00". Section B.2.3 of the spec says that for + /// sequential DCTs, those bytes (8-bit Ss, 8-bit Se, 4-bit Ah, 4-bit Al) + /// should be 0x00, 0x3f, 0x00<<4 | 0x00. + /// + // The C# compiler emits this as a compile-time constant embedded in the PE file. + // This is effectively compiled down to: return new ReadOnlySpan(&data, length) + // More details can be found: https://github.com/dotnet/roslyn/pull/24621 + private static ReadOnlySpan SosHeaderYCbCr => new byte[] + { + JpegConstants.Markers.XFF, JpegConstants.Markers.SOS, + + // Marker + 0x00, 0x0c, + + // Length (high byte, low byte), must be 6 + 2 * (number of components in scan) + 0x03, // Number of components in a scan, 3 + 0x01, // Component Id Y + 0x00, // DC/AC Huffman table + 0x02, // Component Id Cb + 0x11, // DC/AC Huffman table + 0x03, // Component Id Cr + 0x11, // DC/AC Huffman table + 0x00, // Ss - Start of spectral selection. + 0x3f, // Se - End of spectral selection. + 0x00 + + // Ah + Ah (Successive approximation bit position high + low) + }; + + /// + /// Gets the unscaled quantization tables in zig-zag order. Each + /// encoder copies and scales the tables according to its quality parameter. + /// The values are derived from section K.1 after converting from natural to + /// zig-zag order. + /// + // The C# compiler emits this as a compile-time constant embedded in the PE file. + // This is effectively compiled down to: return new ReadOnlySpan(&data, length) + // More details can be found: https://github.com/dotnet/roslyn/pull/24621 + private static ReadOnlySpan UnscaledQuant_Luminance => new byte[] + { + // Luminance. + 16, 11, 12, 14, 12, 10, 16, 14, 13, 14, 18, 17, 16, 19, 24, + 40, 26, 24, 22, 22, 24, 49, 35, 37, 29, 40, 58, 51, 61, 60, + 57, 51, 56, 55, 64, 72, 92, 78, 64, 68, 87, 69, 55, 56, 80, + 109, 81, 87, 95, 98, 103, 104, 103, 62, 77, 113, 121, 112, + 100, 120, 92, 101, 103, 99, + }; + + /// + /// Gets the unscaled quantization tables in zig-zag order. Each + /// encoder copies and scales the tables according to its quality parameter. + /// The values are derived from section K.1 after converting from natural to + /// zig-zag order. + /// + // The C# compiler emits this as a compile-time constant embedded in the PE file. + // This is effectively compiled down to: return new ReadOnlySpan(&data, length) + // More details can be found: https://github.com/dotnet/roslyn/pull/24621 + private static ReadOnlySpan UnscaledQuant_Chrominance => new byte[] + { + // Chrominance. + 17, 18, 18, 24, 21, 24, 47, 26, 26, 47, 99, 66, 56, 66, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + }; + /// /// Encode writes the image to the jpeg baseline format with the given options. /// @@ -259,9 +277,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// The quantization table. private static void InitQuantizationTable(int i, int scale, ref Block8x8F quant) { + DebugGuard.MustBeBetweenOrEqualTo(i, 0, 1, nameof(i)); + var unscaledQuant = (i == 0) ? UnscaledQuant_Luminance : UnscaledQuant_Chrominance; + for (int j = 0; j < Block8x8F.Size; j++) { - int x = UnscaledQuant[i, j]; + int x = unscaledQuant[j]; x = ((x * scale) + 50) / 100; if (x < 1) { @@ -357,7 +378,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg } else { - bt = 8 + BitCountLut[a >> 8]; + bt = 8 + (uint)BitCountLut[a >> 8]; } this.EmitHuff(index, (int)((uint)(runLength << 4) | bt)); @@ -871,7 +892,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg { // TODO: Need a JpegScanEncoder class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.) // TODO: We should allow grayscale writing. - this.outputStream.Write(SosHeaderYCbCr, 0, SosHeaderYCbCr.Length); + this.outputStream.Write(SosHeaderYCbCr); switch (this.subsample) { From 9d7fd6315394e3286eb097ec64f1bc5c4c550183 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 26 Mar 2019 04:29:08 -0700 Subject: [PATCH 031/223] Remove enum boxing allocations (#863) --- src/ImageSharp/Formats/Gif/GifEncoderCore.cs | 2 +- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs index a922f30117..c8580144ad 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs @@ -92,7 +92,7 @@ namespace SixLabors.ImageSharp.Formats.Gif ImageMetaData metaData = image.MetaData; this.gifMetaData = metaData.GetFormatMetaData(GifFormat.Instance); this.colorTableMode = this.colorTableMode ?? this.gifMetaData.ColorTableMode; - bool useGlobalTable = this.colorTableMode.Equals(GifColorTableMode.Global); + bool useGlobalTable = this.colorTableMode == GifColorTableMode.Global; // Quantize the image returning a palette. QuantizedFrame quantized = diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 216f3129f9..96e2dd946d 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -185,7 +185,7 @@ namespace SixLabors.ImageSharp.Formats.Png this.pngColorType = options.ColorType; // Specification recommends default filter method None for paletted images and Paeth for others. - this.pngFilterMethod = options.FilterMethod ?? (options.ColorType.Equals(PngColorType.Palette) + this.pngFilterMethod = options.FilterMethod ?? (options.ColorType == PngColorType.Palette ? PngFilterMethod.None : PngFilterMethod.Paeth); this.compressionLevel = options.CompressionLevel; @@ -217,7 +217,7 @@ namespace SixLabors.ImageSharp.Formats.Png this.writeGamma = this.gamma > 0; this.pngColorType = this.pngColorType ?? pngMetaData.ColorType; this.pngBitDepth = this.pngBitDepth ?? pngMetaData.BitDepth; - this.use16Bit = this.pngBitDepth.Equals(PngBitDepth.Bit16); + this.use16Bit = this.pngBitDepth == PngBitDepth.Bit16; // Ensure we are not allowing impossible combinations. if (!ColorTypes.ContainsKey(this.pngColorType.Value)) @@ -329,7 +329,7 @@ namespace SixLabors.ImageSharp.Formats.Png Span rawScanlineSpan = this.rawScanline.GetSpan(); ref byte rawScanlineSpanRef = ref MemoryMarshal.GetReference(rawScanlineSpan); - if (this.pngColorType.Equals(PngColorType.Grayscale)) + if (this.pngColorType == PngColorType.Grayscale) { if (this.use16Bit) { @@ -761,7 +761,7 @@ namespace SixLabors.ImageSharp.Formats.Png private void WriteTransparencyChunk(Stream stream, PngMetaData pngMetaData) { Span alpha = this.chunkDataBuffer.AsSpan(); - if (pngMetaData.ColorType.Equals(PngColorType.Rgb)) + if (pngMetaData.ColorType == PngColorType.Rgb) { if (pngMetaData.TransparentRgb48.HasValue && this.use16Bit) { @@ -782,7 +782,7 @@ namespace SixLabors.ImageSharp.Formats.Png this.WriteChunk(stream, PngChunkType.Transparency, this.chunkDataBuffer, 0, 6); } } - else if (pngMetaData.ColorType.Equals(PngColorType.Grayscale)) + else if (pngMetaData.ColorType == PngColorType.Grayscale) { if (pngMetaData.TransparentGray16.HasValue && this.use16Bit) { From 56f00399f5ebff7335fc4278bd07d64ebbdd9011 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 31 Mar 2019 22:55:14 +0200 Subject: [PATCH 032/223] make .FromVector4() destructive --- ImageSharp.sln.DotSettings | 1 + src/ImageSharp/Common/Helpers/Guard.cs | 20 +++++++++++++++++++ .../Argb32.PixelOperations.Generated.cs | 4 ++-- .../Bgr24.PixelOperations.Generated.cs | 4 ++-- .../Bgra32.PixelOperations.Generated.cs | 4 ++-- .../Rgb24.PixelOperations.Generated.cs | 4 ++-- .../Generated/_Common.ttinclude | 4 ++-- .../Rgba32.PixelOperations.cs | 4 ++-- .../RgbaVector.PixelOperations.cs | 2 +- .../PixelFormats/PixelOperations{TPixel}.cs | 12 +++++++++-- 10 files changed, 44 insertions(+), 15 deletions(-) diff --git a/ImageSharp.sln.DotSettings b/ImageSharp.sln.DotSettings index 432f4524a0..8e7b5dd488 100644 --- a/ImageSharp.sln.DotSettings +++ b/ImageSharp.sln.DotSettings @@ -382,6 +382,7 @@ True True True + True True True True diff --git a/src/ImageSharp/Common/Helpers/Guard.cs b/src/ImageSharp/Common/Helpers/Guard.cs index d8cf69a52e..e86da78e30 100644 --- a/src/ImageSharp/Common/Helpers/Guard.cs +++ b/src/ImageSharp/Common/Helpers/Guard.cs @@ -257,6 +257,26 @@ namespace SixLabors.ImageSharp } } + /// + /// Verifies that the 'destination' span is not shorter than 'source'. + /// + /// The source element type + /// The destination element type + /// The source span + /// The destination span + /// The name of the argument for 'destination' + [MethodImpl(InliningOptions.ShortMethod)] + public static void DestinationShouldNotBeTooShort( + Span source, + Span destination, + string destinationParamName) + { + if (destination.Length < source.Length) + { + ThrowArgumentException($"Destination span is too short!", destinationParamName); + } + } + /// /// Verifies, that the `source` span has the length of 'minLength', or longer. /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.cs index 2ec965dfb0..add48cfd88 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.cs @@ -42,7 +42,7 @@ namespace SixLabors.ImageSharp.PixelFormats } /// - internal override void FromVector4(Configuration configuration, ReadOnlySpan sourceVectors, Span destPixels) + internal override void FromVector4(Configuration configuration, Span sourceVectors, Span destPixels) { Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, false); } @@ -54,7 +54,7 @@ namespace SixLabors.ImageSharp.PixelFormats } /// - internal override void FromScaledVector4(Configuration configuration, ReadOnlySpan sourceVectors, Span destPixels) + internal override void FromScaledVector4(Configuration configuration, Span sourceVectors, Span destPixels) { Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, true); } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.cs index 711a9d1c16..372c71b32b 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.cs @@ -42,7 +42,7 @@ namespace SixLabors.ImageSharp.PixelFormats } /// - internal override void FromVector4(Configuration configuration, ReadOnlySpan sourceVectors, Span destPixels) + internal override void FromVector4(Configuration configuration, Span sourceVectors, Span destPixels) { Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, false); } @@ -54,7 +54,7 @@ namespace SixLabors.ImageSharp.PixelFormats } /// - internal override void FromScaledVector4(Configuration configuration, ReadOnlySpan sourceVectors, Span destPixels) + internal override void FromScaledVector4(Configuration configuration, Span sourceVectors, Span destPixels) { Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, true); } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.cs index b669dd5348..aa4680dccb 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.cs @@ -42,7 +42,7 @@ namespace SixLabors.ImageSharp.PixelFormats } /// - internal override void FromVector4(Configuration configuration, ReadOnlySpan sourceVectors, Span destPixels) + internal override void FromVector4(Configuration configuration, Span sourceVectors, Span destPixels) { Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, false); } @@ -54,7 +54,7 @@ namespace SixLabors.ImageSharp.PixelFormats } /// - internal override void FromScaledVector4(Configuration configuration, ReadOnlySpan sourceVectors, Span destPixels) + internal override void FromScaledVector4(Configuration configuration, Span sourceVectors, Span destPixels) { Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, true); } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs index dbf3102c4a..f87ed88e56 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs @@ -42,7 +42,7 @@ namespace SixLabors.ImageSharp.PixelFormats } /// - internal override void FromVector4(Configuration configuration, ReadOnlySpan sourceVectors, Span destPixels) + internal override void FromVector4(Configuration configuration, Span sourceVectors, Span destPixels) { Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, false); } @@ -54,7 +54,7 @@ namespace SixLabors.ImageSharp.PixelFormats } /// - internal override void FromScaledVector4(Configuration configuration, ReadOnlySpan sourceVectors, Span destPixels) + internal override void FromScaledVector4(Configuration configuration, Span sourceVectors, Span destPixels) { Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, true); } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/_Common.ttinclude b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/_Common.ttinclude index f0675cb5b3..bfeccfca56 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/_Common.ttinclude +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/_Common.ttinclude @@ -111,7 +111,7 @@ using System.Runtime.InteropServices; { #> /// - internal override void FromVector4(Configuration configuration, ReadOnlySpan sourceVectors, Span<<#=pixelType#>> destPixels) + internal override void FromVector4(Configuration configuration, Span sourceVectors, Span<<#=pixelType#>> destPixels) { Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, false); } @@ -123,7 +123,7 @@ using System.Runtime.InteropServices; } /// - internal override void FromScaledVector4(Configuration configuration, ReadOnlySpan sourceVectors, Span<<#=pixelType#>> destPixels) + internal override void FromScaledVector4(Configuration configuration, Span sourceVectors, Span<<#=pixelType#>> destPixels) { Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, true); } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.PixelOperations.cs index 004b25cd33..2801b82257 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.PixelOperations.cs @@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// internal override void FromVector4( Configuration configuration, - ReadOnlySpan sourceVectors, + Span sourceVectors, Span destPixels) { Guard.DestinationShouldNotBeTooShort(sourceVectors, destPixels, nameof(destPixels)); @@ -60,7 +60,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// internal override void FromScaledVector4( Configuration configuration, - ReadOnlySpan sourceVectors, + Span sourceVectors, Span destinationColors) { this.FromVector4(configuration, sourceVectors, destinationColors); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.PixelOperations.cs index bffaf57ddd..0cde00736a 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.PixelOperations.cs @@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// internal override void FromScaledVector4( Configuration configuration, - ReadOnlySpan sourceVectors, + Span sourceVectors, Span destinationColors) { Guard.DestinationShouldNotBeTooShort(sourceVectors, destinationColors, nameof(destinationColors)); diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs index 115dd7a43d..61e338b9c0 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs @@ -9,6 +9,14 @@ using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.PixelFormats { + internal enum PixelConversionModifiers + { + None = 0, + Scale = 1 << 0, + Premultiply = 1 << 1, + SRgbCompand = 1 << 2, + } + /// /// A stateless class implementing Strategy Pattern for batched pixel-data conversion operations /// for pixel buffers of type . @@ -30,7 +38,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// The to the destination colors. internal virtual void FromVector4( Configuration configuration, - ReadOnlySpan sourceVectors, + Span sourceVectors, Span destPixels) { Guard.NotNull(configuration, nameof(configuration)); @@ -64,7 +72,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// The to the destination colors. internal virtual void FromScaledVector4( Configuration configuration, - ReadOnlySpan sourceVectors, + Span sourceVectors, Span destinationColors) { Guard.NotNull(configuration, nameof(configuration)); From 58573bbc229157269eff161d2de2317fa0562586 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 31 Mar 2019 23:28:34 +0200 Subject: [PATCH 033/223] replace FromScaledVector4() / ToScaledVector4() with flag usages --- .../Argb32.PixelOperations.Generated.cs | 17 +--- .../Bgr24.PixelOperations.Generated.cs | 17 +--- .../Bgra32.PixelOperations.Generated.cs | 17 +--- .../Rgb24.PixelOperations.Generated.cs | 17 +--- .../Generated/_Common.ttinclude | 17 +--- .../Rgba32.PixelOperations.cs | 24 +---- .../RgbaVector.PixelOperations.cs | 35 +++++--- .../PixelFormats/PixelOperations{TPixel}.cs | 88 ++++++++++++------- .../Utils/Vector4Converters.RgbaCompatible.cs | 4 +- 9 files changed, 97 insertions(+), 139 deletions(-) diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.cs index add48cfd88..e832bc97f0 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.cs @@ -42,29 +42,16 @@ namespace SixLabors.ImageSharp.PixelFormats } /// - internal override void FromVector4(Configuration configuration, Span sourceVectors, Span destPixels) + internal override void FromVector4(Configuration configuration, Span sourceVectors, Span destPixels, PixelConversionModifiers modifiers) { Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, false); } /// - internal override void ToVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors) + internal override void ToVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors, PixelConversionModifiers modifiers) { Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, false); } - - /// - internal override void FromScaledVector4(Configuration configuration, Span sourceVectors, Span destPixels) - { - Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, true); - } - - /// - internal override void ToScaledVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors) - { - Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, true); - } - /// internal override void ToRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) { diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.cs index 372c71b32b..f1885c71ba 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.cs @@ -42,30 +42,17 @@ namespace SixLabors.ImageSharp.PixelFormats } /// - internal override void FromVector4(Configuration configuration, Span sourceVectors, Span destPixels) + internal override void FromVector4(Configuration configuration, Span sourceVectors, Span destPixels, PixelConversionModifiers modifiers) { Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, false); } /// - internal override void ToVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors) + internal override void ToVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors, PixelConversionModifiers modifiers) { Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, false); } - /// - internal override void FromScaledVector4(Configuration configuration, Span sourceVectors, Span destPixels) - { - Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, true); - } - - /// - internal override void ToScaledVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors) - { - Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, true); - } - - /// internal override void ToArgb32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) { diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.cs index aa4680dccb..2409f6784b 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.cs @@ -42,29 +42,16 @@ namespace SixLabors.ImageSharp.PixelFormats } /// - internal override void FromVector4(Configuration configuration, Span sourceVectors, Span destPixels) + internal override void FromVector4(Configuration configuration, Span sourceVectors, Span destPixels, PixelConversionModifiers modifiers) { Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, false); } /// - internal override void ToVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors) + internal override void ToVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors, PixelConversionModifiers modifiers) { Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, false); } - - /// - internal override void FromScaledVector4(Configuration configuration, Span sourceVectors, Span destPixels) - { - Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, true); - } - - /// - internal override void ToScaledVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors) - { - Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, true); - } - /// internal override void ToRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) { diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs index f87ed88e56..ddf9813ab4 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs @@ -42,30 +42,17 @@ namespace SixLabors.ImageSharp.PixelFormats } /// - internal override void FromVector4(Configuration configuration, Span sourceVectors, Span destPixels) + internal override void FromVector4(Configuration configuration, Span sourceVectors, Span destPixels, PixelConversionModifiers modifiers) { Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, false); } /// - internal override void ToVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors) + internal override void ToVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors, PixelConversionModifiers modifiers) { Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, false); } - /// - internal override void FromScaledVector4(Configuration configuration, Span sourceVectors, Span destPixels) - { - Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, true); - } - - /// - internal override void ToScaledVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors) - { - Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, true); - } - - /// internal override void ToArgb32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) { diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/_Common.ttinclude b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/_Common.ttinclude index bfeccfca56..0ff5b8605c 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/_Common.ttinclude +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/_Common.ttinclude @@ -111,29 +111,16 @@ using System.Runtime.InteropServices; { #> /// - internal override void FromVector4(Configuration configuration, Span sourceVectors, Span<<#=pixelType#>> destPixels) + internal override void FromVector4(Configuration configuration, Span sourceVectors, Span<<#=pixelType#>> destPixels, PixelConversionModifiers modifiers) { Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, false); } /// - internal override void ToVector4(Configuration configuration, ReadOnlySpan<<#=pixelType#>> sourcePixels, Span destVectors) + internal override void ToVector4(Configuration configuration, ReadOnlySpan<<#=pixelType#>> sourcePixels, Span destVectors, PixelConversionModifiers modifiers) { Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, false); } - - /// - internal override void FromScaledVector4(Configuration configuration, Span sourceVectors, Span<<#=pixelType#>> destPixels) - { - Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, true); - } - - /// - internal override void ToScaledVector4(Configuration configuration, ReadOnlySpan<<#=pixelType#>> sourcePixels, Span destVectors) - { - Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, true); - } - <#+ } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.PixelOperations.cs index 2801b82257..7e8bb7f290 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.PixelOperations.cs @@ -22,7 +22,8 @@ namespace SixLabors.ImageSharp.PixelFormats internal override void ToVector4( Configuration configuration, ReadOnlySpan sourcePixels, - Span destVectors) + Span destVectors, + PixelConversionModifiers modifiers) { Guard.DestinationShouldNotBeTooShort(sourcePixels, destVectors, nameof(destVectors)); @@ -37,7 +38,8 @@ namespace SixLabors.ImageSharp.PixelFormats internal override void FromVector4( Configuration configuration, Span sourceVectors, - Span destPixels) + Span destPixels, + PixelConversionModifiers modifiers) { Guard.DestinationShouldNotBeTooShort(sourceVectors, destPixels, nameof(destPixels)); @@ -47,24 +49,6 @@ namespace SixLabors.ImageSharp.PixelFormats MemoryMarshal.Cast(sourceVectors), MemoryMarshal.Cast(destPixels)); } - - /// - internal override void ToScaledVector4( - Configuration configuration, - ReadOnlySpan sourceColors, - Span destinationVectors) - { - this.ToVector4(configuration, sourceColors, destinationVectors); - } - - /// - internal override void FromScaledVector4( - Configuration configuration, - Span sourceVectors, - Span destinationColors) - { - this.FromVector4(configuration, sourceVectors, destinationColors); - } } } } \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.PixelOperations.cs index 0cde00736a..777d0da6dd 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.PixelOperations.cs @@ -18,32 +18,43 @@ namespace SixLabors.ImageSharp.PixelFormats internal class PixelOperations : PixelOperations { /// - internal override void FromScaledVector4( + internal override void FromVector4( Configuration configuration, Span sourceVectors, - Span destinationColors) + Span destinationColors, + PixelConversionModifiers modifiers) { Guard.DestinationShouldNotBeTooShort(sourceVectors, destinationColors, nameof(destinationColors)); - MemoryMarshal.Cast(sourceVectors).CopyTo(destinationColors); + // TODO: Repeating previous override behavior here. Not sure if this is correct! + if (modifiers.IsDefined(PixelConversionModifiers.Scale)) + { + MemoryMarshal.Cast(sourceVectors).CopyTo(destinationColors); + } + else + { + base.FromVector4(configuration, sourceVectors, destinationColors, modifiers); + } } - /// - internal override void ToScaledVector4( - Configuration configuration, - ReadOnlySpan sourceColors, - Span destinationVectors) - => this.ToVector4(configuration, sourceColors, destinationVectors); - /// internal override void ToVector4( Configuration configuration, ReadOnlySpan sourcePixels, - Span destVectors) + Span destVectors, + PixelConversionModifiers modifiers) { Guard.DestinationShouldNotBeTooShort(sourcePixels, destVectors, nameof(destVectors)); - MemoryMarshal.Cast(sourcePixels).CopyTo(destVectors); + // TODO: Repeating previous override behavior here. Not sure if this is correct! + if (modifiers.IsDefined(PixelConversionModifiers.Scale)) + { + base.ToVector4(configuration, sourcePixels, destVectors, modifiers); + } + else + { + MemoryMarshal.Cast(sourcePixels).CopyTo(destVectors); + } } } } diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs index 61e338b9c0..44c7f2e30f 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs @@ -17,6 +17,12 @@ namespace SixLabors.ImageSharp.PixelFormats SRgbCompand = 1 << 2, } + internal static class PixelConversionModifiersExtensions + { + public static bool IsDefined(this PixelConversionModifiers modifiers, PixelConversionModifiers expected) => + (modifiers & expected) == expected; + } + /// /// A stateless class implementing Strategy Pattern for batched pixel-data conversion operations /// for pixel buffers of type . @@ -32,71 +38,93 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// Bulk version of converting 'sourceVectors.Length' pixels into 'destinationColors'. + /// TODO: Rename to DestructiveFromVector4() + add explain behavior /// /// A to configure internal operations /// The to the source vectors. /// The to the destination colors. + /// The to apply during the conversion internal virtual void FromVector4( Configuration configuration, Span sourceVectors, - Span destPixels) + Span destPixels, + PixelConversionModifiers modifiers) { Guard.NotNull(configuration, nameof(configuration)); Guard.DestinationShouldNotBeTooShort(sourceVectors, destPixels, nameof(destPixels)); - Utils.Vector4Converters.Default.DangerousFromVector4(sourceVectors, destPixels); + if (modifiers.IsDefined(PixelConversionModifiers.Scale)) + { + Utils.Vector4Converters.Default.DangerousFromScaledVector4(sourceVectors, destPixels); + } + else + { + Utils.Vector4Converters.Default.DangerousFromVector4(sourceVectors, destPixels); + } } + /// + /// TODO: For compatibility, should be inlined! + /// + internal void FromScaledVector4( + Configuration configuration, + Span sourceVectors, + Span destPixels) => + this.FromVector4(configuration, sourceVectors, destPixels, PixelConversionModifiers.Scale); + + /// + /// Bulk version of converting 'sourceVectors.Length' pixels into 'destinationColors'. + /// TODO: Rename to DestructiveFromVector4() + add explain behavior + /// + /// A to configure internal operations + /// The to the source vectors. + /// The to the destination colors. + internal void FromVector4(Configuration configuration, Span sourceVectors, Span destPixels) => + this.FromVector4(configuration, sourceVectors, destPixels, PixelConversionModifiers.None); + /// /// Bulk version of converting 'sourceColors.Length' pixels into 'destinationVectors'. /// /// A to configure internal operations /// The to the source colors. /// The to the destination vectors. + /// The to apply during the conversion internal virtual void ToVector4( Configuration configuration, ReadOnlySpan sourcePixels, - Span destVectors) + Span destVectors, + PixelConversionModifiers modifiers) { Guard.NotNull(configuration, nameof(configuration)); Guard.DestinationShouldNotBeTooShort(sourcePixels, destVectors, nameof(destVectors)); - Utils.Vector4Converters.Default.DangerousToVector4(sourcePixels, destVectors); + if (modifiers.IsDefined(PixelConversionModifiers.Scale)) + { + Utils.Vector4Converters.Default.DangerousToScaledVector4(sourcePixels, destVectors); + } + else + { + Utils.Vector4Converters.Default.DangerousToVector4(sourcePixels, destVectors); + } } /// - /// Bulk version of converting 'sourceVectors.Length' pixels into 'destinationColors'. + /// Bulk version of converting 'sourceColors.Length' pixels into 'destinationVectors'. /// /// A to configure internal operations - /// The to the source vectors. - /// The to the destination colors. - internal virtual void FromScaledVector4( + /// The to the source colors. + /// The to the destination vectors. + internal virtual void ToVector4( Configuration configuration, - Span sourceVectors, - Span destinationColors) - { - Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourceVectors, destinationColors, nameof(destinationColors)); - - Utils.Vector4Converters.Default.DangerousFromScaledVector4(sourceVectors, destinationColors); - } + ReadOnlySpan sourcePixels, + Span destVectors) => + this.ToVector4(configuration, sourcePixels, destVectors, PixelConversionModifiers.None); /// - /// Bulk version of converting 'sourceColors.Length' pixels into 'destinationVectors'. + /// TODO: For compatibility, should be inlined! /// - /// A to configure internal operations - /// The to the source colors. - /// The to the destination vectors. - internal virtual void ToScaledVector4( - Configuration configuration, - ReadOnlySpan sourceColors, - Span destinationVectors) - { - Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourceColors, destinationVectors, nameof(destinationVectors)); - - Utils.Vector4Converters.Default.DangerousToScaledVector4(sourceColors, destinationVectors); - } + internal void ToScaledVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors) => + this.ToVector4(configuration, sourcePixels, destVectors, PixelConversionModifiers.Scale); /// /// Converts 'sourceColors.Length' pixels from 'sourceColors' into 'destinationColors'. diff --git a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs index 06c33d79c7..006ebe8284 100644 --- a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs +++ b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs @@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils private static readonly int Vector4ConversionThreshold = CalculateVector4ConversionThreshold(); /// - /// Provides an efficient default implementation for + /// Provides an efficient default implementation for /// and /// which is applicable for -compatible pixel types where /// returns the same scaled result as . @@ -73,7 +73,7 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils } /// - /// Provides an efficient default implementation for + /// Provides an efficient default implementation for /// and /// which is applicable for -compatible pixel types where /// returns the same scaled result as . From f637506bedab6270dab4985bffb872b2d9972486 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 31 Mar 2019 23:40:56 +0200 Subject: [PATCH 034/223] copy companding refactor from #847 --- .../ColorSpaces/Companding/SRgbCompanding.cs | 24 +++++++++++-------- .../PixelFormats/PixelConversionModifiers.cs | 16 +++++++++++++ .../PixelConversionModifiersExtensions.cs | 20 ++++++++++++++++ .../PixelFormats/PixelOperations{TPixel}.cs | 14 ----------- .../Colorspaces/Companding/CompandingTests.cs | 20 ++++++++++++++-- 5 files changed, 68 insertions(+), 26 deletions(-) create mode 100644 src/ImageSharp/PixelFormats/PixelConversionModifiers.cs create mode 100644 src/ImageSharp/PixelFormats/PixelConversionModifiersExtensions.cs diff --git a/src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs b/src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs index 5ae4629137..bbd454fbe8 100644 --- a/src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs +++ b/src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs @@ -30,9 +30,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding for (int i = 0; i < vectors.Length; i++) { ref Vector4 v = ref Unsafe.Add(ref baseRef, i); - v.X = Expand(v.X); - v.Y = Expand(v.Y); - v.Z = Expand(v.Z); + Expand(ref v); } } @@ -48,9 +46,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding for (int i = 0; i < vectors.Length; i++) { ref Vector4 v = ref Unsafe.Add(ref baseRef, i); - v.X = Compress(v.X); - v.Y = Compress(v.Y); - v.Z = Compress(v.Z); + Compress(ref v); } } @@ -58,17 +54,25 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding /// Expands a companded vector to its linear equivalent with respect to the energy. /// /// The vector. - /// The representing the linear channel values. [MethodImpl(InliningOptions.ShortMethod)] - public static Vector4 Expand(Vector4 vector) => new Vector4(Expand(vector.X), Expand(vector.Y), Expand(vector.Z), vector.W); + public static void Expand(ref Vector4 vector) + { + vector.X = Expand(vector.X); + vector.Y = Expand(vector.Y); + vector.Z = Expand(vector.Z); + } /// /// Compresses an uncompanded vector (linear) to its nonlinear equivalent. /// /// The vector. - /// The representing the nonlinear channel values. [MethodImpl(InliningOptions.ShortMethod)] - public static Vector4 Compress(Vector4 vector) => new Vector4(Compress(vector.X), Compress(vector.Y), Compress(vector.Z), vector.W); + public static void Compress(ref Vector4 vector) + { + vector.X = Compress(vector.X); + vector.Y = Compress(vector.Y); + vector.Z = Compress(vector.Z); + } /// /// Expands a companded channel to its linear equivalent with respect to the energy. diff --git a/src/ImageSharp/PixelFormats/PixelConversionModifiers.cs b/src/ImageSharp/PixelFormats/PixelConversionModifiers.cs new file mode 100644 index 0000000000..536a23061a --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelConversionModifiers.cs @@ -0,0 +1,16 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +namespace SixLabors.ImageSharp.PixelFormats +{ + [Flags] + internal enum PixelConversionModifiers + { + None = 0, + Scale = 1 << 0, + Premultiply = 1 << 1, + SRgbCompand = 1 << 2, + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelConversionModifiersExtensions.cs b/src/ImageSharp/PixelFormats/PixelConversionModifiersExtensions.cs new file mode 100644 index 0000000000..bf77f85114 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelConversionModifiersExtensions.cs @@ -0,0 +1,20 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.PixelFormats +{ + internal static class PixelConversionModifiersExtensions + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsDefined(this PixelConversionModifiers modifiers, PixelConversionModifiers expected) => + (modifiers & expected) == expected; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PixelConversionModifiers Remove( + this PixelConversionModifiers modifiers, + PixelConversionModifiers removeThis) => + modifiers & ~removeThis; + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs index 44c7f2e30f..ea5bc876fe 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs @@ -9,20 +9,6 @@ using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.PixelFormats { - internal enum PixelConversionModifiers - { - None = 0, - Scale = 1 << 0, - Premultiply = 1 << 1, - SRgbCompand = 1 << 2, - } - - internal static class PixelConversionModifiersExtensions - { - public static bool IsDefined(this PixelConversionModifiers modifiers, PixelConversionModifiers expected) => - (modifiers & expected) == expected; - } - /// /// A stateless class implementing Strategy Pattern for batched pixel-data conversion operations /// for pixel buffers of type . diff --git a/tests/ImageSharp.Tests/Colorspaces/Companding/CompandingTests.cs b/tests/ImageSharp.Tests/Colorspaces/Companding/CompandingTests.cs index 91cacfe3f0..b2ee030483 100644 --- a/tests/ImageSharp.Tests/Colorspaces/Companding/CompandingTests.cs +++ b/tests/ImageSharp.Tests/Colorspaces/Companding/CompandingTests.cs @@ -53,7 +53,15 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces.Companding { var rnd = new Random(42); Vector4[] source = rnd.GenerateRandomVectorArray(length, 0, 1); - Vector4[] expected = source.Select(v => SRgbCompanding.Expand(v)).ToArray(); + var expected = new Vector4[source.Length]; + + for (int i = 0; i < source.Length; i++) + { + Vector4 s = source[i]; + ref Vector4 e = ref expected[i]; + SRgbCompanding.Expand(ref s); + e = s; + } SRgbCompanding.Expand(source); @@ -68,7 +76,15 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces.Companding { var rnd = new Random(42); Vector4[] source = rnd.GenerateRandomVectorArray(length, 0, 1); - Vector4[] expected = source.Select(v => SRgbCompanding.Compress(v)).ToArray(); + var expected = new Vector4[source.Length]; + + for (int i = 0; i < source.Length; i++) + { + Vector4 s = source[i]; + ref Vector4 e = ref expected[i]; + SRgbCompanding.Compress(ref s); + e = s; + } SRgbCompanding.Compress(source); From b4cb22220392117b55b217c6a96ec85a86bfdaf3 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 31 Mar 2019 23:49:43 +0200 Subject: [PATCH 035/223] add (failing) tests from #847 --- ...xelOperationsTests.Rgb24OperationsTests.cs | 23 ++ .../PixelOperations/PixelOperationsTests.cs | 270 +++++++++++++++++- 2 files changed, 280 insertions(+), 13 deletions(-) create mode 100644 tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Rgb24OperationsTests.cs diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Rgb24OperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Rgb24OperationsTests.cs new file mode 100644 index 0000000000..fa51c2278b --- /dev/null +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Rgb24OperationsTests.cs @@ -0,0 +1,23 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +using Xunit.Abstractions; + +namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations +{ + public partial class PixelOperationsTests + { + public class Rgb24OperationsTests : PixelOperationsTests + { + public Rgb24OperationsTests(ITestOutputHelper output) + : base(output) + { + } + + [Fact] + public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs index d9845e4741..9433e19c9b 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs @@ -6,7 +6,7 @@ using System.Buffers; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; - +using SixLabors.ImageSharp.ColorSpaces.Companding; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -70,25 +70,33 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations internal static PixelOperations Operations => PixelOperations.Instance; - internal static TPixel[] CreateExpectedPixelData(Vector4[] source) + internal static TPixel[] CreateExpectedPixelData(Vector4[] source, RefAction vectorModifier = null) { var expected = new TPixel[source.Length]; for (int i = 0; i < expected.Length; i++) { - expected[i].FromVector4(source[i]); + Vector4 v = source[i]; + vectorModifier?.Invoke(ref v); + + expected[i].FromVector4(v); } + return expected; } - internal static TPixel[] CreateScaledExpectedPixelData(Vector4[] source) + internal static TPixel[] CreateScaledExpectedPixelData(Vector4[] source, RefAction vectorModifier = null) { var expected = new TPixel[source.Length]; for (int i = 0; i < expected.Length; i++) { - expected[i].FromScaledVector4(source[i]); + Vector4 v = source[i]; + vectorModifier?.Invoke(ref v); + + expected[i].FromScaledVector4(v); } + return expected; } @@ -120,6 +128,112 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations ); } + [Theory] + [MemberData(nameof(ArraySizesData))] + public void FromCompandedScaledVector4(int count) + { + void sourceAction(ref Vector4 v) + { + SRgbCompanding.Expand(ref v); + } + + void expectedAction(ref Vector4 v) + { + SRgbCompanding.Compress(ref v); + } + + Vector4[] source = CreateVector4TestData(count, (ref Vector4 v) => sourceAction(ref v)); + TPixel[] expected = CreateScaledExpectedPixelData(source, (ref Vector4 v) => expectedAction(ref v)); + + TestOperation( + source, + expected, + (s, d) => Operations.FromVector4(this.Configuration, s, d.GetSpan(), PixelConversionModifiers.SRgbCompand | PixelConversionModifiers.Scale) + ); + } + + [Theory] + [MemberData(nameof(ArraySizesData))] + public void FromPremultipliedVector4(int count) + { + void sourceAction(ref Vector4 v) + { + Vector4Utils.Premultiply(ref v); + } + + void expectedAction(ref Vector4 v) + { + Vector4Utils.UnPremultiply(ref v); + } + + Vector4[] source = CreateVector4TestData(count, (ref Vector4 v) => sourceAction(ref v)); + TPixel[] expected = CreateExpectedPixelData(source, (ref Vector4 v) => expectedAction(ref v)); + + TestOperation( + source, + expected, + (s, d) => Operations.FromVector4(this.Configuration, s, d.GetSpan(), PixelConversionModifiers.Premultiply) + ); + } + + [Theory] + [MemberData(nameof(ArraySizesData))] + public void FromPremultipliedScaledVector4(int count) + { + void sourceAction(ref Vector4 v) + { + Vector4Utils.Premultiply(ref v); + } + + void expectedAction(ref Vector4 v) + { + Vector4Utils.UnPremultiply(ref v); + } + + Vector4[] source = CreateVector4TestData(count, (ref Vector4 v) => sourceAction(ref v)); + TPixel[] expected = CreateScaledExpectedPixelData(source, (ref Vector4 v) => expectedAction(ref v)); + + TestOperation( + source, + expected, + (s, d) => Operations.FromVector4( + this.Configuration, + s, + d.GetSpan(), + PixelConversionModifiers.Premultiply | PixelConversionModifiers.Scale) + ); + } + + [Theory] + [MemberData(nameof(ArraySizesData))] + public void FromCompandedPremultipliedScaledVector4(int count) + { + void sourceAction(ref Vector4 v) + { + SRgbCompanding.Expand(ref v); + Vector4Utils.Premultiply(ref v); + } + + void expectedAction(ref Vector4 v) + { + Vector4Utils.UnPremultiply(ref v); + SRgbCompanding.Compress(ref v); + } + + Vector4[] source = CreateVector4TestData(count, (ref Vector4 v) => sourceAction(ref v)); + TPixel[] expected = CreateScaledExpectedPixelData(source, (ref Vector4 v) => expectedAction(ref v)); + + TestOperation( + source, + expected, + (s, d) => Operations.FromVector4( + this.Configuration, + s, + d.GetSpan(), + PixelConversionModifiers.SRgbCompand | PixelConversionModifiers.Premultiply) + ); + } + [Theory] [MemberData(nameof(ArraySizesData))] public void ToVector4(int count) @@ -148,6 +262,115 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations ); } + [Theory] + [MemberData(nameof(ArraySizesData))] + public void ToCompandedScaledVector4(int count) + { + void sourceAction(ref Vector4 v) + { + SRgbCompanding.Compress(ref v); + } + + void expectedAction(ref Vector4 v) + { + SRgbCompanding.Expand(ref v); + } + + TPixel[] source = CreateScaledPixelTestData(count, (ref Vector4 v) => sourceAction(ref v)); + Vector4[] expected = CreateExpectedScaledVector4Data(source, (ref Vector4 v) => expectedAction(ref v)); + + TestOperation( + source, + expected, + (s, d) => Operations.ToVector4( + this.Configuration, + s, + d.GetSpan(), + PixelConversionModifiers.SRgbCompand | PixelConversionModifiers.Scale) + ); + } + + [Theory] + [MemberData(nameof(ArraySizesData))] + public void ToPremultipliedVector4(int count) + { + void sourceAction(ref Vector4 v) + { + Vector4Utils.UnPremultiply(ref v); + } + + void expectedAction(ref Vector4 v) + { + Vector4Utils.Premultiply(ref v); + } + + TPixel[] source = CreatePixelTestData(count, (ref Vector4 v) => sourceAction(ref v)); + Vector4[] expected = CreateExpectedVector4Data(source, (ref Vector4 v) => expectedAction(ref v)); + + TestOperation( + source, + expected, + (s, d) => Operations.ToVector4(this.Configuration, s, d.GetSpan(), PixelConversionModifiers.Premultiply) + ); + } + + [Theory] + [MemberData(nameof(ArraySizesData))] + public void ToPremultipliedScaledVector4(int count) + { + void sourceAction(ref Vector4 v) + { + Vector4Utils.UnPremultiply(ref v); + } + + void expectedAction(ref Vector4 v) + { + Vector4Utils.Premultiply(ref v); + } + + TPixel[] source = CreateScaledPixelTestData(count, (ref Vector4 v) => sourceAction(ref v)); + Vector4[] expected = CreateExpectedScaledVector4Data(source, (ref Vector4 v) => expectedAction(ref v)); + + TestOperation( + source, + expected, + (s, d) => Operations.ToVector4( + this.Configuration, + s, + d.GetSpan(), + PixelConversionModifiers.Premultiply | PixelConversionModifiers.Scale)); + } + + [Theory] + [MemberData(nameof(ArraySizesData))] + public void ToCompandedPremultipliedScaledVector4(int count) + { + void sourceAction(ref Vector4 v) + { + Vector4Utils.UnPremultiply(ref v); + SRgbCompanding.Compress(ref v); + } + + void expectedAction(ref Vector4 v) + { + SRgbCompanding.Expand(ref v); + Vector4Utils.Premultiply(ref v); + } + + TPixel[] source = CreateScaledPixelTestData(count, (ref Vector4 v) => sourceAction(ref v)); + Vector4[] expected = CreateExpectedScaledVector4Data(source, (ref Vector4 v) => expectedAction(ref v)); + + TestOperation( + source, + expected, + (s, d) => Operations.ToVector4( + this.Configuration, + s, + d.GetSpan(), + PixelConversionModifiers.SRgbCompand | PixelConversionModifiers.Premultiply | PixelConversionModifiers.Scale) + ); + } + [Theory] [MemberData(nameof(ArraySizesData))] public void FromArgb32Bytes(int count) @@ -477,25 +700,37 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations ); } - internal static Vector4[] CreateExpectedVector4Data(TPixel[] source) + public delegate void RefAction(ref T1 arg1); + + internal static Vector4[] CreateExpectedVector4Data(TPixel[] source, RefAction vectorModifier = null) { var expected = new Vector4[source.Length]; for (int i = 0; i < expected.Length; i++) { - expected[i] = source[i].ToVector4(); + var v = source[i].ToVector4(); + + vectorModifier?.Invoke(ref v); + + expected[i] = v; } + return expected; } - internal static Vector4[] CreateExpectedScaledVector4Data(TPixel[] source) + internal static Vector4[] CreateExpectedScaledVector4Data(TPixel[] source, RefAction vectorModifier = null) { var expected = new Vector4[source.Length]; for (int i = 0; i < expected.Length; i++) { - expected[i] = source[i].ToScaledVector4(); + Vector4 v = source[i].ToScaledVector4(); + + vectorModifier?.Invoke(ref v); + + expected[i] = v; } + return expected; } @@ -513,19 +748,22 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations } } - internal static Vector4[] CreateVector4TestData(int length) + internal static Vector4[] CreateVector4TestData(int length, RefAction vectorModifier = null) { var result = new Vector4[length]; var rnd = new Random(42); // Deterministic random values for (int i = 0; i < result.Length; i++) { - result[i] = GetVector(rnd); + Vector4 v = GetVector(rnd); + vectorModifier?.Invoke(ref v); + + result[i] = v; } return result; } - internal static TPixel[] CreatePixelTestData(int length) + internal static TPixel[] CreatePixelTestData(int length, RefAction vectorModifier = null) { var result = new TPixel[length]; @@ -534,13 +772,16 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations for (int i = 0; i < result.Length; i++) { Vector4 v = GetVector(rnd); + + vectorModifier?.Invoke(ref v); + result[i].FromVector4(v); } return result; } - internal static TPixel[] CreateScaledPixelTestData(int length) + internal static TPixel[] CreateScaledPixelTestData(int length, RefAction vectorModifier = null) { var result = new TPixel[length]; @@ -549,6 +790,9 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations for (int i = 0; i < result.Length; i++) { Vector4 v = GetVector(rnd); + + vectorModifier?.Invoke(ref v); + result[i].FromScaledVector4(v); } From c3d4e4d8f4585b2325017bc7ccf0d428bc41841d Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 1 Apr 2019 00:56:11 +0200 Subject: [PATCH 036/223] all pixel types implemented except RgbaVector --- .../Argb32.PixelOperations.Generated.cs | 4 +- .../Bgr24.PixelOperations.Generated.cs | 4 +- .../Bgra32.PixelOperations.Generated.cs | 4 +- .../Rgb24.PixelOperations.Generated.cs | 4 +- .../Generated/_Common.ttinclude | 4 +- .../Rgba32.PixelOperations.cs | 6 +- .../RgbaVector.PixelOperations.cs | 6 ++ .../PixelFormats/PixelOperations{TPixel}.cs | 20 +---- .../Utils/Vector4Converters.Default.cs | 74 +++++++++++++++++-- .../Utils/Vector4Converters.RgbaCompatible.cs | 46 ++++-------- .../PixelFormats/Utils/Vector4Converters.cs | 48 ++++++++++++ .../PixelOperations/PixelOperationsTests.cs | 8 +- 12 files changed, 158 insertions(+), 70 deletions(-) create mode 100644 src/ImageSharp/PixelFormats/Utils/Vector4Converters.cs diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.cs index e832bc97f0..c49f3f8343 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.cs @@ -44,13 +44,13 @@ namespace SixLabors.ImageSharp.PixelFormats /// internal override void FromVector4(Configuration configuration, Span sourceVectors, Span destPixels, PixelConversionModifiers modifiers) { - Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, false); + Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, modifiers.Remove(PixelConversionModifiers.Scale)); } /// internal override void ToVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors, PixelConversionModifiers modifiers) { - Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, false); + Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, modifiers.Remove(PixelConversionModifiers.Scale)); } /// internal override void ToRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.cs index f1885c71ba..a9eca8994c 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.cs @@ -44,13 +44,13 @@ namespace SixLabors.ImageSharp.PixelFormats /// internal override void FromVector4(Configuration configuration, Span sourceVectors, Span destPixels, PixelConversionModifiers modifiers) { - Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, false); + Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, modifiers.Remove(PixelConversionModifiers.Scale)); } /// internal override void ToVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors, PixelConversionModifiers modifiers) { - Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, false); + Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, modifiers.Remove(PixelConversionModifiers.Scale)); } /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.cs index 2409f6784b..73eddc235e 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.cs @@ -44,13 +44,13 @@ namespace SixLabors.ImageSharp.PixelFormats /// internal override void FromVector4(Configuration configuration, Span sourceVectors, Span destPixels, PixelConversionModifiers modifiers) { - Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, false); + Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, modifiers.Remove(PixelConversionModifiers.Scale)); } /// internal override void ToVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors, PixelConversionModifiers modifiers) { - Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, false); + Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, modifiers.Remove(PixelConversionModifiers.Scale)); } /// internal override void ToRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs index ddf9813ab4..1648f53416 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs @@ -44,13 +44,13 @@ namespace SixLabors.ImageSharp.PixelFormats /// internal override void FromVector4(Configuration configuration, Span sourceVectors, Span destPixels, PixelConversionModifiers modifiers) { - Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, false); + Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, modifiers.Remove(PixelConversionModifiers.Scale)); } /// internal override void ToVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors, PixelConversionModifiers modifiers) { - Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, false); + Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, modifiers.Remove(PixelConversionModifiers.Scale)); } /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/_Common.ttinclude b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/_Common.ttinclude index 0ff5b8605c..6286eaf72c 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/_Common.ttinclude +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/_Common.ttinclude @@ -113,13 +113,13 @@ using System.Runtime.InteropServices; /// internal override void FromVector4(Configuration configuration, Span sourceVectors, Span<<#=pixelType#>> destPixels, PixelConversionModifiers modifiers) { - Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, false); + Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, modifiers.Remove(PixelConversionModifiers.Scale)); } /// internal override void ToVector4(Configuration configuration, ReadOnlySpan<<#=pixelType#>> sourcePixels, Span destVectors, PixelConversionModifiers modifiers) { - Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, false); + Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, modifiers.Remove(PixelConversionModifiers.Scale)); } <#+ } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.PixelOperations.cs index 7e8bb7f290..aa9b82be7d 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.PixelOperations.cs @@ -4,6 +4,8 @@ using System; using System.Numerics; using System.Runtime.InteropServices; + +using SixLabors.ImageSharp.PixelFormats.Utils; using SixLabors.Memory; namespace SixLabors.ImageSharp.PixelFormats @@ -28,10 +30,10 @@ namespace SixLabors.ImageSharp.PixelFormats Guard.DestinationShouldNotBeTooShort(sourcePixels, destVectors, nameof(destVectors)); destVectors = destVectors.Slice(0, sourcePixels.Length); - SimdUtils.BulkConvertByteToNormalizedFloat( MemoryMarshal.Cast(sourcePixels), MemoryMarshal.Cast(destVectors)); + Vector4Converters.ApplyForwardConversionModifiers(destVectors, modifiers); } /// @@ -44,7 +46,7 @@ namespace SixLabors.ImageSharp.PixelFormats Guard.DestinationShouldNotBeTooShort(sourceVectors, destPixels, nameof(destPixels)); destPixels = destPixels.Slice(0, sourceVectors.Length); - + Vector4Converters.ApplyBackwardConversionModifiers(sourceVectors, modifiers); SimdUtils.BulkConvertNormalizedFloatToByteClampOverflows( MemoryMarshal.Cast(sourceVectors), MemoryMarshal.Cast(destPixels)); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.PixelOperations.cs index 777d0da6dd..3c4d951ee7 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.PixelOperations.cs @@ -5,6 +5,8 @@ using System; using System.Numerics; using System.Runtime.InteropServices; +using SixLabors.ImageSharp.PixelFormats.Utils; + namespace SixLabors.ImageSharp.PixelFormats { /// @@ -26,6 +28,8 @@ namespace SixLabors.ImageSharp.PixelFormats { Guard.DestinationShouldNotBeTooShort(sourceVectors, destinationColors, nameof(destinationColors)); + Vector4Converters.ApplyBackwardConversionModifiers(sourceVectors, modifiers); + // TODO: Repeating previous override behavior here. Not sure if this is correct! if (modifiers.IsDefined(PixelConversionModifiers.Scale)) { @@ -55,6 +59,8 @@ namespace SixLabors.ImageSharp.PixelFormats { MemoryMarshal.Cast(sourcePixels).CopyTo(destVectors); } + + Vector4Converters.ApplyForwardConversionModifiers(destVectors, modifiers); } } } diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs index ea5bc876fe..53181e7a94 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs @@ -37,16 +37,8 @@ namespace SixLabors.ImageSharp.PixelFormats PixelConversionModifiers modifiers) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourceVectors, destPixels, nameof(destPixels)); - if (modifiers.IsDefined(PixelConversionModifiers.Scale)) - { - Utils.Vector4Converters.Default.DangerousFromScaledVector4(sourceVectors, destPixels); - } - else - { - Utils.Vector4Converters.Default.DangerousFromVector4(sourceVectors, destPixels); - } + Utils.Vector4Converters.Default.FromVector4(sourceVectors, destPixels, modifiers); } /// @@ -82,16 +74,8 @@ namespace SixLabors.ImageSharp.PixelFormats PixelConversionModifiers modifiers) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destVectors, nameof(destVectors)); - if (modifiers.IsDefined(PixelConversionModifiers.Scale)) - { - Utils.Vector4Converters.Default.DangerousToScaledVector4(sourcePixels, destVectors); - } - else - { - Utils.Vector4Converters.Default.DangerousToVector4(sourcePixels, destVectors); - } + Utils.Vector4Converters.Default.ToVector4(sourcePixels, destVectors, modifiers); } /// diff --git a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs index e784e3b5a6..4c4e60276d 100644 --- a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs +++ b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs @@ -6,6 +6,8 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using SixLabors.ImageSharp.ColorSpaces.Companding; + namespace SixLabors.ImageSharp.PixelFormats.Utils { /// @@ -15,13 +17,75 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils { /// /// Provides default implementations for batched to/from conversion. - /// WARNING: The methods are operating without bounds checking and input validation! + /// WARNING: The methods prefixed with "Unsafe" are operating without bounds checking and input validation! /// Input validation is the responsibility of the caller! /// public static class Default { [MethodImpl(InliningOptions.ShortMethod)] - internal static void DangerousFromVector4( + public static void FromVector4( + Span sourceVectors, + Span destPixels, + PixelConversionModifiers modifiers) + where TPixel : struct, IPixel + { + Guard.DestinationShouldNotBeTooShort(sourceVectors, destPixels, nameof(destPixels)); + + UnsafeFromVector4(sourceVectors, destPixels, modifiers); + } + + [MethodImpl(InliningOptions.ShortMethod)] + public static void ToVector4( + ReadOnlySpan sourcePixels, + Span destVectors, + PixelConversionModifiers modifiers) + where TPixel : struct, IPixel + { + Guard.DestinationShouldNotBeTooShort(sourcePixels, destVectors, nameof(destVectors)); + + UnsafeToVector4(sourcePixels, destVectors, modifiers); + } + + [MethodImpl(InliningOptions.ShortMethod)] + public static void UnsafeFromVector4( + Span sourceVectors, + Span destPixels, + PixelConversionModifiers modifiers) + where TPixel : struct, IPixel + { + ApplyBackwardConversionModifiers(sourceVectors, modifiers); + + if (modifiers.IsDefined(PixelConversionModifiers.Scale)) + { + UnsafeFromScaledVector4Core(sourceVectors, destPixels); + } + else + { + UnsafeFromVector4Core(sourceVectors, destPixels); + } + } + + [MethodImpl(InliningOptions.ShortMethod)] + public static void UnsafeToVector4( + ReadOnlySpan sourcePixels, + Span destVectors, + PixelConversionModifiers modifiers) + where TPixel : struct, IPixel + { + if (modifiers.IsDefined(PixelConversionModifiers.Scale)) + { + UnsafeToScaledVector4Core(sourcePixels, destVectors); + } + else + { + UnsafeToVector4Core(sourcePixels, destVectors); + } + + ApplyForwardConversionModifiers(destVectors, modifiers); + } + + [MethodImpl(InliningOptions.ShortMethod)] + private static void UnsafeFromVector4Core( ReadOnlySpan sourceVectors, Span destPixels) where TPixel : struct, IPixel @@ -38,7 +102,7 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils } [MethodImpl(InliningOptions.ShortMethod)] - internal static void DangerousToVector4( + private static void UnsafeToVector4Core( ReadOnlySpan sourcePixels, Span destVectors) where TPixel : struct, IPixel @@ -55,7 +119,7 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils } [MethodImpl(InliningOptions.ShortMethod)] - internal static void DangerousFromScaledVector4( + private static void UnsafeFromScaledVector4Core( ReadOnlySpan sourceVectors, Span destinationColors) where TPixel : struct, IPixel @@ -72,7 +136,7 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils } [MethodImpl(InliningOptions.ShortMethod)] - internal static void DangerousToScaledVector4( + private static void UnsafeToScaledVector4Core( ReadOnlySpan sourceColors, Span destinationVectors) where TPixel : struct, IPixel diff --git a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs index 006ebe8284..efe4dc69a6 100644 --- a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs +++ b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs @@ -7,6 +7,8 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using SixLabors.ImageSharp.ColorSpaces.Companding; + namespace SixLabors.ImageSharp.PixelFormats.Utils { /// @@ -41,7 +43,7 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils PixelOperations pixelOperations, ReadOnlySpan sourcePixels, Span destVectors, - bool scaled) + PixelConversionModifiers modifiers) where TPixel : struct, IPixel { Guard.NotNull(configuration, nameof(configuration)); @@ -52,7 +54,7 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils // Not worth for small buffers: if (count < Vector4ConversionThreshold) { - ToVector4Fallback(sourcePixels, destVectors, scaled); + Default.UnsafeToVector4(sourcePixels, destVectors, modifiers); return; } @@ -70,6 +72,9 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils MemoryMarshal.Cast(destVectors.Slice(0, countWithoutLastItem))); destVectors[countWithoutLastItem] = sourcePixels[countWithoutLastItem].ToVector4(); + + // TODO: Investigate optimized 1-pass approach! + ApplyForwardConversionModifiers(destVectors, modifiers); } /// @@ -83,9 +88,9 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils internal static void FromVector4( Configuration configuration, PixelOperations pixelOperations, - ReadOnlySpan sourceVectors, + Span sourceVectors, Span destPixels, - bool scaled) + PixelConversionModifiers modifiers) where TPixel : struct, IPixel { Guard.NotNull(configuration, nameof(configuration)); @@ -96,11 +101,14 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils // Not worth for small buffers: if (count < Vector4ConversionThreshold) { - FromVector4Fallback(sourceVectors, destPixels, scaled); + Default.UnsafeFromVector4(sourceVectors, destPixels, modifiers); return; } + // TODO: Investigate optimized 1-pass approach! + ApplyBackwardConversionModifiers(sourceVectors, modifiers); + // For the opposite direction it's not easy to implement the trick used in RunRgba32CompatibleToVector4Conversion, // so let's allocate a temporary buffer as usually: using (IMemoryOwner tempBuffer = configuration.MemoryAllocator.Allocate(count)) @@ -115,34 +123,6 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils } } - [MethodImpl(InliningOptions.ColdPath)] - private static void ToVector4Fallback(ReadOnlySpan sourcePixels, Span destVectors, bool scaled) - where TPixel : struct, IPixel - { - if (scaled) - { - Default.DangerousToScaledVector4(sourcePixels, destVectors); - } - else - { - Default.DangerousToVector4(sourcePixels, destVectors); - } - } - - [MethodImpl(InliningOptions.ColdPath)] - private static void FromVector4Fallback(ReadOnlySpan sourceVectors, Span destPixels, bool scaled) - where TPixel : struct, IPixel - { - if (scaled) - { - Default.DangerousFromScaledVector4(sourceVectors, destPixels); - } - else - { - Default.DangerousFromVector4(sourceVectors, destPixels); - } - } - private static int CalculateVector4ConversionThreshold() { if (!Vector.IsHardwareAccelerated) diff --git a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.cs b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.cs new file mode 100644 index 0000000000..447869a7d5 --- /dev/null +++ b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.cs @@ -0,0 +1,48 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +using SixLabors.ImageSharp.ColorSpaces.Companding; + +namespace SixLabors.ImageSharp.PixelFormats.Utils +{ + internal static partial class Vector4Converters + { + /// + /// Apply modifiers used requested by ToVector4() conversion. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void ApplyForwardConversionModifiers(Span vectors, PixelConversionModifiers modifiers) + { + if (modifiers.IsDefined(PixelConversionModifiers.SRgbCompand)) + { + SRgbCompanding.Expand(vectors); + } + + if (modifiers.IsDefined(PixelConversionModifiers.Premultiply)) + { + Vector4Utils.Premultiply(vectors); + } + } + + /// + /// Apply modifiers used requested by FromVector4() conversion. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void ApplyBackwardConversionModifiers(Span vectors, PixelConversionModifiers modifiers) + { + if (modifiers.IsDefined(PixelConversionModifiers.Premultiply)) + { + Vector4Utils.UnPremultiply(vectors); + } + + if (modifiers.IsDefined(PixelConversionModifiers.SRgbCompand)) + { + SRgbCompanding.Compress(vectors); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs index 9433e19c9b..f51c790348 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs @@ -148,7 +148,11 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations TestOperation( source, expected, - (s, d) => Operations.FromVector4(this.Configuration, s, d.GetSpan(), PixelConversionModifiers.SRgbCompand | PixelConversionModifiers.Scale) + (s, d) => Operations.FromVector4( + this.Configuration, + s, + d.GetSpan(), + PixelConversionModifiers.SRgbCompand | PixelConversionModifiers.Scale) ); } @@ -230,7 +234,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations this.Configuration, s, d.GetSpan(), - PixelConversionModifiers.SRgbCompand | PixelConversionModifiers.Premultiply) + PixelConversionModifiers.SRgbCompand | PixelConversionModifiers.Premultiply | PixelConversionModifiers.Scale) ); } From d33be1cba4e6e1e61eec897820586e2eac187db8 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 1 Apr 2019 01:00:34 +0200 Subject: [PATCH 037/223] fix RgbaVector --- .../RgbaVector.PixelOperations.cs | 22 ++----------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.PixelOperations.cs index 3c4d951ee7..2d52f98cb6 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.PixelOperations.cs @@ -29,16 +29,7 @@ namespace SixLabors.ImageSharp.PixelFormats Guard.DestinationShouldNotBeTooShort(sourceVectors, destinationColors, nameof(destinationColors)); Vector4Converters.ApplyBackwardConversionModifiers(sourceVectors, modifiers); - - // TODO: Repeating previous override behavior here. Not sure if this is correct! - if (modifiers.IsDefined(PixelConversionModifiers.Scale)) - { - MemoryMarshal.Cast(sourceVectors).CopyTo(destinationColors); - } - else - { - base.FromVector4(configuration, sourceVectors, destinationColors, modifiers); - } + MemoryMarshal.Cast(sourceVectors).CopyTo(destinationColors); } /// @@ -50,16 +41,7 @@ namespace SixLabors.ImageSharp.PixelFormats { Guard.DestinationShouldNotBeTooShort(sourcePixels, destVectors, nameof(destVectors)); - // TODO: Repeating previous override behavior here. Not sure if this is correct! - if (modifiers.IsDefined(PixelConversionModifiers.Scale)) - { - base.ToVector4(configuration, sourcePixels, destVectors, modifiers); - } - else - { - MemoryMarshal.Cast(sourcePixels).CopyTo(destVectors); - } - + MemoryMarshal.Cast(sourcePixels).CopyTo(destVectors); Vector4Converters.ApplyForwardConversionModifiers(destVectors, modifiers); } } From fd3d76e78e146e459f179c6c9f4bb321db3400d5 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 1 Apr 2019 01:05:59 +0200 Subject: [PATCH 038/223] Inlining temporary compatibility methods (FromScaledVector4, ToScaledVector4) --- .../PixelFormats/PixelBlender{TPixel}.cs | 36 +++++++------------ .../PixelFormats/PixelOperations{TPixel}.cs | 15 -------- .../Utils/Vector4Converters.RgbaCompatible.cs | 8 +---- .../Dithering/PaletteDitherProcessorBase.cs | 2 +- .../FrameQuantizerBase{TPixel}.cs | 2 +- .../PixelOperations/PixelOperationsTests.cs | 14 +++++--- .../TestUtilities/TestImageExtensions.cs | 4 +-- 7 files changed, 27 insertions(+), 54 deletions(-) diff --git a/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs index 5c8e506ae1..e814984633 100644 --- a/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs @@ -112,21 +112,15 @@ namespace SixLabors.ImageSharp.PixelFormats Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); - PixelOperations.Instance.ToScaledVector4( - configuration, - background.Slice(0, background.Length), - backgroundSpan); - PixelOperations.Instance.ToScaledVector4( - configuration, - source.Slice(0, background.Length), - sourceSpan); + ReadOnlySpan sourcePixels = background.Slice(0, background.Length); + PixelOperations.Instance.ToVector4(configuration, sourcePixels, backgroundSpan, PixelConversionModifiers.Scale); + ReadOnlySpan sourcePixels1 = source.Slice(0, background.Length); + PixelOperations.Instance.ToVector4(configuration, sourcePixels1, sourceSpan, PixelConversionModifiers.Scale); this.BlendFunction(destinationSpan, backgroundSpan, sourceSpan, amount); - PixelOperations.Instance.FromScaledVector4( - configuration, - destinationSpan.Slice(0, background.Length), - destination); + Span sourceVectors = destinationSpan.Slice(0, background.Length); + PixelOperations.Instance.FromVector4(configuration, sourceVectors, destination, PixelConversionModifiers.Scale); } } @@ -161,21 +155,15 @@ namespace SixLabors.ImageSharp.PixelFormats Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); - PixelOperations.Instance.ToScaledVector4( - configuration, - background.Slice(0, background.Length), - backgroundSpan); - PixelOperations.Instance.ToScaledVector4( - configuration, - source.Slice(0, background.Length), - sourceSpan); + ReadOnlySpan sourcePixels = background.Slice(0, background.Length); + PixelOperations.Instance.ToVector4(configuration, sourcePixels, backgroundSpan, PixelConversionModifiers.Scale); + ReadOnlySpan sourcePixels1 = source.Slice(0, background.Length); + PixelOperations.Instance.ToVector4(configuration, sourcePixels1, sourceSpan, PixelConversionModifiers.Scale); this.BlendFunction(destinationSpan, backgroundSpan, sourceSpan, amount); - PixelOperations.Instance.FromScaledVector4( - configuration, - destinationSpan.Slice(0, background.Length), - destination); + Span sourceVectors = destinationSpan.Slice(0, background.Length); + PixelOperations.Instance.FromVector4(configuration, sourceVectors, destination, PixelConversionModifiers.Scale); } } } diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs index 53181e7a94..6dae22c5ad 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs @@ -41,15 +41,6 @@ namespace SixLabors.ImageSharp.PixelFormats Utils.Vector4Converters.Default.FromVector4(sourceVectors, destPixels, modifiers); } - /// - /// TODO: For compatibility, should be inlined! - /// - internal void FromScaledVector4( - Configuration configuration, - Span sourceVectors, - Span destPixels) => - this.FromVector4(configuration, sourceVectors, destPixels, PixelConversionModifiers.Scale); - /// /// Bulk version of converting 'sourceVectors.Length' pixels into 'destinationColors'. /// TODO: Rename to DestructiveFromVector4() + add explain behavior @@ -90,12 +81,6 @@ namespace SixLabors.ImageSharp.PixelFormats Span destVectors) => this.ToVector4(configuration, sourcePixels, destVectors, PixelConversionModifiers.None); - /// - /// TODO: For compatibility, should be inlined! - /// - internal void ToScaledVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors) => - this.ToVector4(configuration, sourcePixels, destVectors, PixelConversionModifiers.Scale); - /// /// Converts 'sourceColors.Length' pixels from 'sourceColors' into 'destinationColors'. /// diff --git a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs index efe4dc69a6..5188a89a36 100644 --- a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs +++ b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs @@ -32,10 +32,7 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils /// /// Provides an efficient default implementation for - /// and - /// which is applicable for -compatible pixel types where - /// returns the same scaled result as . - /// The method is works by internally converting to a therefore it's not applicable for that type! + /// The method works by internally converting to a therefore it's not applicable for that type! /// [MethodImpl(InliningOptions.ShortMethod)] internal static void ToVector4( @@ -79,9 +76,6 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils /// /// Provides an efficient default implementation for - /// and - /// which is applicable for -compatible pixel types where - /// returns the same scaled result as . /// The method is works by internally converting to a therefore it's not applicable for that type! /// [MethodImpl(InliningOptions.ShortMethod)] diff --git a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessorBase.cs b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessorBase.cs index 43f6c8a454..6313b74c94 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessorBase.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessorBase.cs @@ -98,7 +98,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering if (this.paletteVector is null) { this.paletteVector = new Vector4[this.Palette.Length]; - PixelOperations.Instance.ToScaledVector4(configuration, this.Palette, this.paletteVector); + PixelOperations.Instance.ToVector4(configuration, (ReadOnlySpan)this.Palette, (Span)this.paletteVector, PixelConversionModifiers.Scale); } } } diff --git a/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizerBase{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizerBase{TPixel}.cs index 3b9b046a00..a70cfb6601 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizerBase{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizerBase{TPixel}.cs @@ -79,7 +79,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization // Collect the palette. Required before the second pass runs. TPixel[] palette = this.GetPalette(); this.paletteVector = new Vector4[palette.Length]; - PixelOperations.Instance.ToScaledVector4(image.Configuration, palette, this.paletteVector); + PixelOperations.Instance.ToVector4(image.Configuration, (ReadOnlySpan)palette, (Span)this.paletteVector, PixelConversionModifiers.Scale); var quantizedFrame = new QuantizedFrame(image.MemoryAllocator, width, height, palette); if (this.Dither) diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs index f51c790348..b9d3fcff5b 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs @@ -124,8 +124,11 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations TestOperation( source, expected, - (s, d) => Operations.FromScaledVector4(this.Configuration, s, d.GetSpan()) - ); + (s, d) => + { + Span destPixels = d.GetSpan(); + Operations.FromVector4(this.Configuration, (Span)s, destPixels, PixelConversionModifiers.Scale); + }); } [Theory] @@ -262,8 +265,11 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations TestOperation( source, expected, - (s, d) => Operations.ToScaledVector4(this.Configuration, s, d.GetSpan()) - ); + (s, d) => + { + Span destVectors = d.GetSpan(); + Operations.ToVector4(this.Configuration, (ReadOnlySpan)s, destVectors, PixelConversionModifiers.Scale); + }); } [Theory] diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index 29d39596b7..ad31319bb9 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Tests { Span pixelSpan = frame.GetPixelSpan(); - PixelOperations.Instance.ToScaledVector4(configuration, pixelSpan, tempSpan); + PixelOperations.Instance.ToVector4(configuration, pixelSpan, tempSpan, PixelConversionModifiers.Scale); for (int i = 0; i < tempSpan.Length; i++) { @@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Tests v.W = 1F; } - PixelOperations.Instance.FromScaledVector4(configuration, tempSpan, pixelSpan); + PixelOperations.Instance.FromVector4(configuration, tempSpan, pixelSpan, PixelConversionModifiers.Scale); } } }); From c88f8707e92e244df51c1b4826222e91f27d4cd4 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 1 Apr 2019 01:42:17 +0200 Subject: [PATCH 039/223] Optimization: omit premultiplication for Bgr24 and Rgb24 --- .../PixelFormats/PixelConversionModifiers.cs | 24 +++++++++++++ .../Bgr24.PixelOperations.Generated.cs | 4 +-- .../Rgb24.PixelOperations.Generated.cs | 4 +-- .../Generated/_Common.ttinclude | 16 ++++++--- ...xelOperationsTests.Bgr24OperationsTests.cs | 1 + ...xelOperationsTests.Rgb24OperationsTests.cs | 1 + .../PixelOperations/PixelOperationsTests.cs | 34 +++++++++++++++---- 7 files changed, 69 insertions(+), 15 deletions(-) diff --git a/src/ImageSharp/PixelFormats/PixelConversionModifiers.cs b/src/ImageSharp/PixelFormats/PixelConversionModifiers.cs index 536a23061a..d66dbb57a1 100644 --- a/src/ImageSharp/PixelFormats/PixelConversionModifiers.cs +++ b/src/ImageSharp/PixelFormats/PixelConversionModifiers.cs @@ -3,14 +3,38 @@ using System; +using SixLabors.ImageSharp.ColorSpaces.Companding; + namespace SixLabors.ImageSharp.PixelFormats { + /// + /// Flags responsible to select additional operations which could be effitiently applied in + /// + /// or + /// + /// knowing the pixel type. + /// [Flags] internal enum PixelConversionModifiers { + /// + /// No special operation is selected + /// None = 0, + + /// + /// Select instead the standard (non scaled) variants. + /// Scale = 1 << 0, + + /// + /// Enable alpha premultiplication / unpremultiplication + /// Premultiply = 1 << 1, + + /// + /// Enable SRGB companding (defined in ). + /// SRgbCompand = 1 << 2, } } \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.cs index a9eca8994c..92925898b6 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.cs @@ -44,13 +44,13 @@ namespace SixLabors.ImageSharp.PixelFormats /// internal override void FromVector4(Configuration configuration, Span sourceVectors, Span destPixels, PixelConversionModifiers modifiers) { - Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, modifiers.Remove(PixelConversionModifiers.Scale)); + Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, modifiers.Remove(PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply)); } /// internal override void ToVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors, PixelConversionModifiers modifiers) { - Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, modifiers.Remove(PixelConversionModifiers.Scale)); + Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, modifiers.Remove(PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply)); } /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs index 1648f53416..6de8c92bcf 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs @@ -44,13 +44,13 @@ namespace SixLabors.ImageSharp.PixelFormats /// internal override void FromVector4(Configuration configuration, Span sourceVectors, Span destPixels, PixelConversionModifiers modifiers) { - Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, modifiers.Remove(PixelConversionModifiers.Scale)); + Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, modifiers.Remove(PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply)); } /// internal override void ToVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors, PixelConversionModifiers modifiers) { - Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, modifiers.Remove(PixelConversionModifiers.Scale)); + Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, modifiers.Remove(PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply)); } /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/_Common.ttinclude b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/_Common.ttinclude index 6286eaf72c..584615532d 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/_Common.ttinclude +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/_Common.ttinclude @@ -22,6 +22,7 @@ using System.Runtime.InteropServices; // Types with Rgba32-combatible to/from Vector4 conversion static readonly string[] Rgba32CompatibleTypes = { "Argb32", "Bgra32", "Rgb24", "Bgr24" }; + void GenerateDefaultSelfConversionMethods(string pixelType) { #> @@ -107,19 +108,24 @@ using System.Runtime.InteropServices; <#+ } - void GenerateRgba32CompatibleVector4ConversionMethods(string pixelType) + void GenerateRgba32CompatibleVector4ConversionMethods(string pixelType, bool hasAlpha) { + string removeTheseModifiers = "PixelConversionModifiers.Scale"; + if (!hasAlpha) + { + removeTheseModifiers += " | PixelConversionModifiers.Premultiply"; + } #> /// internal override void FromVector4(Configuration configuration, Span sourceVectors, Span<<#=pixelType#>> destPixels, PixelConversionModifiers modifiers) { - Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, modifiers.Remove(PixelConversionModifiers.Scale)); + Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, modifiers.Remove(<#=removeTheseModifiers#>)); } /// internal override void ToVector4(Configuration configuration, ReadOnlySpan<<#=pixelType#>> sourcePixels, Span destVectors, PixelConversionModifiers modifiers) { - Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, modifiers.Remove(PixelConversionModifiers.Scale)); + Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, modifiers.Remove(<#=removeTheseModifiers#>)); } <#+ } @@ -130,7 +136,7 @@ using System.Runtime.InteropServices; if (Rgba32CompatibleTypes.Contains(pixelType)) { - GenerateRgba32CompatibleVector4ConversionMethods(pixelType); + GenerateRgba32CompatibleVector4ConversionMethods(pixelType, pixelType.EndsWith("32")); } var matching32BitTypes = Optimized32BitTypes.Contains(pixelType) ? @@ -151,4 +157,4 @@ using System.Runtime.InteropServices; GenerateDefaultConvertToMethod(pixelType, destPixelType); } } -#> \ No newline at end of file +#> diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Bgr24OperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Bgr24OperationsTests.cs index 323d3914cf..afcec79385 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Bgr24OperationsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Bgr24OperationsTests.cs @@ -15,6 +15,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations public Bgr24OperationsTests(ITestOutputHelper output) : base(output) { + this.HasAlpha = false; } [Fact] diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Rgb24OperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Rgb24OperationsTests.cs index fa51c2278b..1c7d312d30 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Rgb24OperationsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Rgb24OperationsTests.cs @@ -14,6 +14,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations public Rgb24OperationsTests(ITestOutputHelper output) : base(output) { + this.HasAlpha = false; } [Fact] diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs index b9d3fcff5b..659da47a7e 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs @@ -33,6 +33,8 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations null; #endif + protected bool HasAlpha { get; set; } = true; + protected PixelOperationsTests(ITestOutputHelper output) : base(output) { @@ -165,12 +167,18 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations { void sourceAction(ref Vector4 v) { - Vector4Utils.Premultiply(ref v); + if (this.HasAlpha) + { + Vector4Utils.Premultiply(ref v); + } } void expectedAction(ref Vector4 v) { - Vector4Utils.UnPremultiply(ref v); + if (this.HasAlpha) + { + Vector4Utils.UnPremultiply(ref v); + } } Vector4[] source = CreateVector4TestData(count, (ref Vector4 v) => sourceAction(ref v)); @@ -189,12 +197,18 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations { void sourceAction(ref Vector4 v) { - Vector4Utils.Premultiply(ref v); + if (this.HasAlpha) + { + Vector4Utils.Premultiply(ref v); + } } void expectedAction(ref Vector4 v) { - Vector4Utils.UnPremultiply(ref v); + if (this.HasAlpha) + { + Vector4Utils.UnPremultiply(ref v); + } } Vector4[] source = CreateVector4TestData(count, (ref Vector4 v) => sourceAction(ref v)); @@ -218,12 +232,20 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations void sourceAction(ref Vector4 v) { SRgbCompanding.Expand(ref v); - Vector4Utils.Premultiply(ref v); + + if (this.HasAlpha) + { + Vector4Utils.Premultiply(ref v); + } } void expectedAction(ref Vector4 v) { - Vector4Utils.UnPremultiply(ref v); + if (this.HasAlpha) + { + Vector4Utils.UnPremultiply(ref v); + } + SRgbCompanding.Compress(ref v); } From 689944db2781d2c635645696f4a71cee4992abc1 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 1 Apr 2019 01:45:39 +0200 Subject: [PATCH 040/223] extend Resize benchmarks --- .../ImageSharp.Benchmarks/Samplers/Resize.cs | 47 ++++++++++++++----- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/tests/ImageSharp.Benchmarks/Samplers/Resize.cs b/tests/ImageSharp.Benchmarks/Samplers/Resize.cs index 148b253281..239e2fc6fb 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/Resize.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/Resize.cs @@ -14,11 +14,12 @@ using SixLabors.ImageSharp.Processing; namespace SixLabors.ImageSharp.Benchmarks { [Config(typeof(Config.ShortClr))] - public abstract class ResizeBenchmarkBase + public abstract class ResizeBenchmarkBase + where TPixel : struct, IPixel { protected readonly Configuration Configuration = new Configuration(new JpegConfigurationModule()); - private Image sourceImage; + private Image sourceImage; private Bitmap sourceBitmap; @@ -31,7 +32,7 @@ namespace SixLabors.ImageSharp.Benchmarks [GlobalSetup] public void Setup() { - this.sourceImage = new Image(this.Configuration, this.SourceSize, this.SourceSize); + this.sourceImage = new Image(this.Configuration, this.SourceSize, this.SourceSize); this.sourceBitmap = new Bitmap(this.SourceSize, this.SourceSize); } @@ -65,26 +66,29 @@ namespace SixLabors.ImageSharp.Benchmarks [Benchmark(Description = "ImageSharp, MaxDegreeOfParallelism = 1")] public int ImageSharp_P1() => this.RunImageSharpResize(1); - [Benchmark(Description = "ImageSharp, MaxDegreeOfParallelism = 4")] - public int ImageSharp_P4() => this.RunImageSharpResize(4); + // Parallel cases have been disabled for fast benchmark execution. + // Uncomment, if you are interested in parallel speedup - [Benchmark(Description = "ImageSharp, MaxDegreeOfParallelism = 8")] - public int ImageSharp_P8() => this.RunImageSharpResize(8); + //[Benchmark(Description = "ImageSharp, MaxDegreeOfParallelism = 4")] + //public int ImageSharp_P4() => this.RunImageSharpResize(4); + + //[Benchmark(Description = "ImageSharp, MaxDegreeOfParallelism = 8")] + //public int ImageSharp_P8() => this.RunImageSharpResize(8); protected int RunImageSharpResize(int maxDegreeOfParallelism) { this.Configuration.MaxDegreeOfParallelism = maxDegreeOfParallelism; - using (Image clone = this.sourceImage.Clone(this.ExecuteResizeOperation)) + using (Image clone = this.sourceImage.Clone(this.ExecuteResizeOperation)) { return clone.Width; } } - protected abstract void ExecuteResizeOperation(IImageProcessingContext ctx); + protected abstract void ExecuteResizeOperation(IImageProcessingContext ctx); } - - public class Resize_Bicubic : ResizeBenchmarkBase + + public class Resize_Bicubic_Rgba32 : ResizeBenchmarkBase { protected override void ExecuteResizeOperation(IImageProcessingContext ctx) { @@ -115,7 +119,15 @@ namespace SixLabors.ImageSharp.Benchmarks } - public class Resize_BicubicCompand : ResizeBenchmarkBase + public class Resize_Bicubic_Bgra32 : ResizeBenchmarkBase + { + protected override void ExecuteResizeOperation(IImageProcessingContext ctx) + { + ctx.Resize(this.DestSize, this.DestSize, KnownResamplers.Bicubic); + } + } + + public class Resize_BicubicCompand_Rgba32 : ResizeBenchmarkBase { protected override void ExecuteResizeOperation(IImageProcessingContext ctx) { @@ -144,4 +156,13 @@ namespace SixLabors.ImageSharp.Benchmarks // 'ImageSharp, MaxDegreeOfParallelism = 4' | Core | 3032 | 400 | 36.21 ms | 53.802 ms | 3.0399 ms | 0.36 | 0.03 | 25300 B | // 'ImageSharp, MaxDegreeOfParallelism = 8' | Core | 3032 | 400 | 26.52 ms | 2.173 ms | 0.1228 ms | 0.27 | 0.00 | 25589 B | } -} + + public class Resize_Bicubic_Rgb24 : ResizeBenchmarkBase + { + protected override void ExecuteResizeOperation(IImageProcessingContext ctx) + { + ctx.Resize(this.DestSize, this.DestSize, KnownResamplers.Bicubic); + } + } + +} \ No newline at end of file From 6cc51ebb8885a4a7e3c9c66cf6f720a47e68b66f Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 1 Apr 2019 01:56:14 +0200 Subject: [PATCH 041/223] update ResizeProcessor --- .../Transforms/Resize/ResizeProcessor.cs | 102 ++++++++---------- tests/Images/External | 2 +- 2 files changed, 48 insertions(+), 56 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs index 189e21de7a..6904122ccf 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs @@ -9,7 +9,6 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.ColorSpaces.Companding; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; @@ -216,27 +215,33 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms workingRect, configuration, rows => + { + for (int y = rows.Min; y < rows.Max; y++) { - for (int y = rows.Min; y < rows.Max; y++) + // Y coordinates of source points + Span sourceRow = + source.GetPixelRowSpan((int)(((y - startY) * heightFactor) + sourceY)); + Span targetRow = destination.GetPixelRowSpan(y); + + for (int x = minX; x < maxX; x++) { - // Y coordinates of source points - Span sourceRow = - source.GetPixelRowSpan((int)(((y - startY) * heightFactor) + sourceY)); - Span targetRow = destination.GetPixelRowSpan(y); - - for (int x = minX; x < maxX; x++) - { - // X coordinates of source points - targetRow[x] = sourceRow[(int)(((x - startX) * widthFactor) + sourceX)]; - } + // X coordinates of source points + targetRow[x] = sourceRow[(int)(((x - startX) * widthFactor) + sourceX)]; } - }); + } + }); return; } int sourceHeight = source.Height; + PixelConversionModifiers conversionModifiers = PixelConversionModifiers.Premultiply; + if (this.Compand) + { + conversionModifiers |= PixelConversionModifiers.Scale | PixelConversionModifiers.SRgbCompand; + } + // Interpolate the image using the calculated weights. // A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm // First process the columns. Since we are not using multiple threads startY and endY @@ -251,30 +256,23 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms processColsRect, configuration, (rows, tempRowBuffer) => + { + for (int y = rows.Min; y < rows.Max; y++) { - for (int y = rows.Min; y < rows.Max; y++) - { - Span sourceRow = source.GetPixelRowSpan(y).Slice(sourceX); - Span tempRowSpan = tempRowBuffer.Span.Slice(sourceX); - - PixelOperations.Instance.ToVector4(configuration, sourceRow, tempRowSpan); - Vector4Utils.Premultiply(tempRowSpan); + Span sourceRow = source.GetPixelRowSpan(y).Slice(sourceX); + Span tempRowSpan = tempRowBuffer.Span.Slice(sourceX); - ref Vector4 firstPassBaseRef = ref firstPassPixelsTransposed.Span[y]; + PixelOperations.Instance.ToVector4(configuration, sourceRow, tempRowSpan, conversionModifiers); - if (this.Compand) - { - SRgbCompanding.Expand(tempRowSpan); - } + ref Vector4 firstPassBaseRef = ref firstPassPixelsTransposed.Span[y]; - for (int x = minX; x < maxX; x++) - { - ResizeKernel kernel = this.horizontalKernelMap.GetKernel(x - startX); - Unsafe.Add(ref firstPassBaseRef, x * sourceHeight) = - kernel.Convolve(tempRowSpan); - } + for (int x = minX; x < maxX; x++) + { + ResizeKernel kernel = this.horizontalKernelMap.GetKernel(x - startX); + Unsafe.Add(ref firstPassBaseRef, x * sourceHeight) = kernel.Convolve(tempRowSpan); } - }); + } + }); var processRowsRect = Rectangle.FromLTRB(0, minY, width, maxY); @@ -283,35 +281,29 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms processRowsRect, configuration, (rows, tempRowBuffer) => - { - Span tempRowSpan = tempRowBuffer.Span; - - for (int y = rows.Min; y < rows.Max; y++) - { - // Ensure offsets are normalized for cropping and padding. - ResizeKernel kernel = this.verticalKernelMap.GetKernel(y - startY); + { + Span tempRowSpan = tempRowBuffer.Span; - ref Vector4 tempRowBase = ref MemoryMarshal.GetReference(tempRowSpan); + for (int y = rows.Min; y < rows.Max; y++) + { + // Ensure offsets are normalized for cropping and padding. + ResizeKernel kernel = this.verticalKernelMap.GetKernel(y - startY); - for (int x = 0; x < width; x++) - { - Span firstPassColumn = firstPassPixelsTransposed.GetRowSpan(x).Slice(sourceY); + ref Vector4 tempRowBase = ref MemoryMarshal.GetReference(tempRowSpan); - // Destination color components - Unsafe.Add(ref tempRowBase, x) = kernel.Convolve(firstPassColumn); - } + for (int x = 0; x < width; x++) + { + Span firstPassColumn = firstPassPixelsTransposed.GetRowSpan(x).Slice(sourceY); - Vector4Utils.UnPremultiply(tempRowSpan); + // Destination color components + Unsafe.Add(ref tempRowBase, x) = kernel.Convolve(firstPassColumn); + } - if (this.Compand) - { - SRgbCompanding.Compress(tempRowSpan); - } + Span targetRowSpan = destination.GetPixelRowSpan(y); - Span targetRowSpan = destination.GetPixelRowSpan(y); - PixelOperations.Instance.FromVector4(configuration, tempRowSpan, targetRowSpan); - } - }); + PixelOperations.Instance.FromVector4(configuration, tempRowSpan, targetRowSpan, conversionModifiers); + } + }); } } diff --git a/tests/Images/External b/tests/Images/External index c4098e463a..4387e56e85 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit c4098e463ab0e7128ae196b7f963369271df8fd3 +Subproject commit 4387e56e8590ee00607efe29a9babfda041621cb From 4d9fb8bff2ff18c5fc38476fe38ff47208e24f39 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 1 Apr 2019 02:06:44 +0200 Subject: [PATCH 042/223] update benchmark results --- .../ImageSharp.Benchmarks/Samplers/Resize.cs | 124 +++++++++++------- 1 file changed, 79 insertions(+), 45 deletions(-) diff --git a/tests/ImageSharp.Benchmarks/Samplers/Resize.cs b/tests/ImageSharp.Benchmarks/Samplers/Resize.cs index 239e2fc6fb..51f3a5653a 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/Resize.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/Resize.cs @@ -95,27 +95,25 @@ namespace SixLabors.ImageSharp.Benchmarks ctx.Resize(this.DestSize, this.DestSize, KnownResamplers.Bicubic); } - // RESULTS (2018 October): + // RESULTS (2019 April): // - // BenchmarkDotNet=v0.10.14, OS=Windows 10.0.17134 + // BenchmarkDotNet=v0.11.3, OS=Windows 10.0.17134.648 (1803/April2018Update/Redstone4) // Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores - // Frequency=2742191 Hz, Resolution=364.6719 ns, Timer=TSC - // .NET Core SDK=2.1.403 - // [Host] : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT - // Job-IGUFBA : .NET Framework 4.7.1 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3190.0 - // Job-DZFERG : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT - // - // Method | Runtime | SourceSize | DestSize | Mean | Error | StdDev | Scaled | ScaledSD | Allocated | - // ----------------------------------------- |-------- |----------- |--------- |----------:|----------:|----------:|-------:|---------:|----------:| - // SystemDrawing | Clr | 3032 | 400 | 101.13 ms | 18.659 ms | 1.0542 ms | 1.00 | 0.00 | 0 B | - // 'ImageSharp, MaxDegreeOfParallelism = 1' | Clr | 3032 | 400 | 122.05 ms | 19.622 ms | 1.1087 ms | 1.21 | 0.01 | 21856 B | - // 'ImageSharp, MaxDegreeOfParallelism = 4' | Clr | 3032 | 400 | 41.34 ms | 54.841 ms | 3.0986 ms | 0.41 | 0.03 | 28000 B | - // 'ImageSharp, MaxDegreeOfParallelism = 8' | Clr | 3032 | 400 | 31.68 ms | 12.782 ms | 0.7222 ms | 0.31 | 0.01 | 28256 B | - // | | | | | | | | | | - // SystemDrawing | Core | 3032 | 400 | 100.37 ms | 18.479 ms | 1.0441 ms | 1.00 | 0.00 | 0 B | - // 'ImageSharp, MaxDegreeOfParallelism = 1' | Core | 3032 | 400 | 73.03 ms | 10.540 ms | 0.5955 ms | 0.73 | 0.01 | 21368 B | - // 'ImageSharp, MaxDegreeOfParallelism = 4' | Core | 3032 | 400 | 22.59 ms | 4.863 ms | 0.2748 ms | 0.23 | 0.00 | 25220 B | - // 'ImageSharp, MaxDegreeOfParallelism = 8' | Core | 3032 | 400 | 21.10 ms | 23.362 ms | 1.3200 ms | 0.21 | 0.01 | 25539 B | + // Frequency=2742192 Hz, Resolution=364.6718 ns, Timer=TSC + // .NET Core SDK=2.1.602 + // [Host] : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT + // Clr : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3362.0 + // Core : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT + // + // IterationCount=3 LaunchCount=1 WarmupCount=3 + // + // Method | Job | Runtime | SourceSize | DestSize | Mean | Error | StdDev | Ratio | Gen 0/1k Op | Gen 1/1k Op | Gen 2/1k Op | Allocated Memory/Op | + // ----------------------------------------- |----- |-------- |----------- |--------- |----------:|----------:|----------:|------:|------------:|------------:|------------:|--------------------:| + // SystemDrawing | Clr | Clr | 3032 | 400 | 118.71 ms | 4.884 ms | 0.2677 ms | 1.00 | - | - | - | 2048 B | + // 'ImageSharp, MaxDegreeOfParallelism = 1' | Clr | Clr | 3032 | 400 | 94.55 ms | 16.160 ms | 0.8858 ms | 0.80 | - | - | - | 16384 B | + // | | | | | | | | | | | | | + // SystemDrawing | Core | Core | 3032 | 400 | 118.38 ms | 2.814 ms | 0.1542 ms | 1.00 | - | - | - | 96 B | + // 'ImageSharp, MaxDegreeOfParallelism = 1' | Core | Core | 3032 | 400 | 90.28 ms | 4.679 ms | 0.2565 ms | 0.76 | - | - | - | 15712 B | } @@ -125,44 +123,80 @@ namespace SixLabors.ImageSharp.Benchmarks { ctx.Resize(this.DestSize, this.DestSize, KnownResamplers.Bicubic); } + + // RESULTS (2019 April): + // + // BenchmarkDotNet=v0.11.3, OS=Windows 10.0.17134.648 (1803/April2018Update/Redstone4) + // Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores + // Frequency=2742192 Hz, Resolution=364.6718 ns, Timer=TSC + // .NET Core SDK=2.1.602 + // [Host] : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT + // Clr : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3362.0 + // Core : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT + // + // IterationCount=3 LaunchCount=1 WarmupCount=3 + // + // Method | Job | Runtime | SourceSize | DestSize | Mean | Error | StdDev | Ratio | Gen 0/1k Op | Gen 1/1k Op | Gen 2/1k Op | Allocated Memory/Op | + // ----------------------------------------- |----- |-------- |----------- |--------- |----------:|----------:|----------:|------:|------------:|------------:|------------:|--------------------:| + // SystemDrawing | Clr | Clr | 3032 | 400 | 119.01 ms | 18.513 ms | 1.0147 ms | 1.00 | - | - | - | 1638 B | + // 'ImageSharp, MaxDegreeOfParallelism = 1' | Clr | Clr | 3032 | 400 | 104.71 ms | 16.078 ms | 0.8813 ms | 0.88 | - | - | - | 45056 B | + // | | | | | | | | | | | | | + // SystemDrawing | Core | Core | 3032 | 400 | 121.58 ms | 50.084 ms | 2.7453 ms | 1.00 | - | - | - | 96 B | + // 'ImageSharp, MaxDegreeOfParallelism = 1' | Core | Core | 3032 | 400 | 96.96 ms | 7.899 ms | 0.4329 ms | 0.80 | - | - | - | 44512 B | } - public class Resize_BicubicCompand_Rgba32 : ResizeBenchmarkBase + public class Resize_Bicubic_Rgb24 : ResizeBenchmarkBase { - protected override void ExecuteResizeOperation(IImageProcessingContext ctx) + protected override void ExecuteResizeOperation(IImageProcessingContext ctx) { - ctx.Resize(this.DestSize, this.DestSize, KnownResamplers.Bicubic, true); + ctx.Resize(this.DestSize, this.DestSize, KnownResamplers.Bicubic); } - // RESULTS (2018 October): + // RESULTS (2019 April): // - // BenchmarkDotNet=v0.10.14, OS=Windows 10.0.17134 + // BenchmarkDotNet=v0.11.3, OS=Windows 10.0.17134.648 (1803/April2018Update/Redstone4) // Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores - // Frequency=2742191 Hz, Resolution=364.6719 ns, Timer=TSC - // .NET Core SDK=2.1.403 - // [Host] : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT - // Job-IGUFBA : .NET Framework 4.7.1 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3190.0 - // Job-DZFERG : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT - // - // Method | Runtime | SourceSize | DestSize | Mean | Error | StdDev | Scaled | ScaledSD | Allocated | - // ----------------------------------------- |-------- |----------- |--------- |----------:|----------:|----------:|-------:|---------:|----------:| - // SystemDrawing | Clr | 3032 | 400 | 100.63 ms | 13.864 ms | 0.7833 ms | 1.00 | 0.00 | 0 B | - // 'ImageSharp, MaxDegreeOfParallelism = 1' | Clr | 3032 | 400 | 156.83 ms | 28.631 ms | 1.6177 ms | 1.56 | 0.02 | 21856 B | - // 'ImageSharp, MaxDegreeOfParallelism = 4' | Clr | 3032 | 400 | 53.43 ms | 38.493 ms | 2.1749 ms | 0.53 | 0.02 | 28512 B | - // 'ImageSharp, MaxDegreeOfParallelism = 8' | Clr | 3032 | 400 | 38.47 ms | 11.969 ms | 0.6763 ms | 0.38 | 0.01 | 28000 B | - // | | | | | | | | | | - // SystemDrawing | Core | 3032 | 400 | 99.87 ms | 23.459 ms | 1.3255 ms | 1.00 | 0.00 | 0 B | - // 'ImageSharp, MaxDegreeOfParallelism = 1' | Core | 3032 | 400 | 108.19 ms | 38.562 ms | 2.1788 ms | 1.08 | 0.02 | 21368 B | - // 'ImageSharp, MaxDegreeOfParallelism = 4' | Core | 3032 | 400 | 36.21 ms | 53.802 ms | 3.0399 ms | 0.36 | 0.03 | 25300 B | - // 'ImageSharp, MaxDegreeOfParallelism = 8' | Core | 3032 | 400 | 26.52 ms | 2.173 ms | 0.1228 ms | 0.27 | 0.00 | 25589 B | + // Frequency=2742192 Hz, Resolution=364.6718 ns, Timer=TSC + // .NET Core SDK=2.1.602 + // [Host] : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT + // Clr : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3362.0 + // Core : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT + // + // Method | Job | Runtime | SourceSize | DestSize | Mean | Error | StdDev | Ratio | RatioSD | Gen 0/1k Op | Gen 1/1k Op | Gen 2/1k Op | Allocated Memory/Op | + // ----------------------------------------- |----- |-------- |----------- |--------- |----------:|----------:|----------:|------:|--------:|------------:|------------:|------------:|--------------------:| + // SystemDrawing | Clr | Clr | 3032 | 400 | 121.37 ms | 48.580 ms | 2.6628 ms | 1.00 | 0.00 | - | - | - | 2048 B | + // 'ImageSharp, MaxDegreeOfParallelism = 1' | Clr | Clr | 3032 | 400 | 99.36 ms | 11.356 ms | 0.6224 ms | 0.82 | 0.02 | - | - | - | 45056 B | + // | | | | | | | | | | | | | | + // SystemDrawing | Core | Core | 3032 | 400 | 118.06 ms | 15.667 ms | 0.8587 ms | 1.00 | 0.00 | - | - | - | 96 B | + // 'ImageSharp, MaxDegreeOfParallelism = 1' | Core | Core | 3032 | 400 | 92.47 ms | 5.683 ms | 0.3115 ms | 0.78 | 0.01 | - | - | - | 44512 B | } - public class Resize_Bicubic_Rgb24 : ResizeBenchmarkBase + + public class Resize_BicubicCompand_Rgba32 : ResizeBenchmarkBase { - protected override void ExecuteResizeOperation(IImageProcessingContext ctx) + protected override void ExecuteResizeOperation(IImageProcessingContext ctx) { - ctx.Resize(this.DestSize, this.DestSize, KnownResamplers.Bicubic); + ctx.Resize(this.DestSize, this.DestSize, KnownResamplers.Bicubic, true); } - } + // RESULTS (2019 April): + // + // BenchmarkDotNet=v0.11.3, OS=Windows 10.0.17134.648 (1803/April2018Update/Redstone4) + // Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores + // Frequency=2742192 Hz, Resolution=364.6718 ns, Timer=TSC + // .NET Core SDK=2.1.602 + // [Host] : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT + // Clr : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3362.0 + // Core : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT + // + // IterationCount=3 LaunchCount=1 WarmupCount=3 + // + // Method | Job | Runtime | SourceSize | DestSize | Mean | Error | StdDev | Ratio | RatioSD | Gen 0/1k Op | Gen 1/1k Op | Gen 2/1k Op | Allocated Memory/Op | + // ----------------------------------------- |----- |-------- |----------- |--------- |---------:|----------:|----------:|------:|--------:|------------:|------------:|------------:|--------------------:| + // SystemDrawing | Clr | Clr | 3032 | 400 | 120.7 ms | 68.985 ms | 3.7813 ms | 1.00 | 0.00 | - | - | - | 1638 B | + // 'ImageSharp, MaxDegreeOfParallelism = 1' | Clr | Clr | 3032 | 400 | 132.2 ms | 15.976 ms | 0.8757 ms | 1.10 | 0.04 | - | - | - | 16384 B | + // | | | | | | | | | | | | | | + // SystemDrawing | Core | Core | 3032 | 400 | 118.3 ms | 6.899 ms | 0.3781 ms | 1.00 | 0.00 | - | - | - | 96 B | + // 'ImageSharp, MaxDegreeOfParallelism = 1' | Core | Core | 3032 | 400 | 122.4 ms | 15.069 ms | 0.8260 ms | 1.03 | 0.01 | - | - | - | 15712 B | + } } \ No newline at end of file From 86ce62ed2cbbefc4a0f6b75cca023a0410ba0f48 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 1 Apr 2019 02:14:16 +0200 Subject: [PATCH 043/223] rename FromVector4(...) to FromVector4Destructive(...) --- .../Decoder/JpegImagePostProcessor.cs | 2 +- .../PixelFormats/PixelBlender{TPixel}.cs | 4 ++-- .../PixelFormats/PixelConversionModifiers.cs | 4 ++-- .../Argb32.PixelOperations.Generated.cs | 2 +- .../Bgr24.PixelOperations.Generated.cs | 2 +- .../Bgra32.PixelOperations.Generated.cs | 2 +- .../Rgb24.PixelOperations.Generated.cs | 2 +- .../Rgba32.PixelOperations.cs | 2 +- .../RgbaVector.PixelOperations.cs | 2 +- .../PixelFormats/PixelOperations{TPixel}.cs | 18 +++++++++++++----- .../Utils/Vector4Converters.RgbaCompatible.cs | 2 +- .../Convolution/Convolution2DProcessor.cs | 2 +- .../Convolution/Convolution2PassProcessor.cs | 2 +- .../Convolution/ConvolutionProcessor.cs | 2 +- .../Processors/Filters/FilterProcessor.cs | 2 +- .../Transforms/AffineTransformProcessor.cs | 2 +- .../Transforms/ProjectiveTransformProcessor.cs | 2 +- .../Transforms/Resize/ResizeProcessor.cs | 2 +- .../Color/Bulk/FromVector4.cs | 4 ++-- .../PixelBlenders/PorterDuffBulkVsPixel.cs | 2 +- .../PixelOperations/PixelOperationsTests.cs | 12 ++++++------ .../TestUtilities/TestImageExtensions.cs | 2 +- 22 files changed, 42 insertions(+), 34 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs index 438749abf9..2c81908843 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs @@ -173,7 +173,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder Span destRow = destination.GetPixelRowSpan(yy); // TODO: Investigate if slicing is actually necessary - PixelOperations.Instance.FromVector4(this.configuration, this.rgbaBuffer.GetSpan().Slice(0, destRow.Length), destRow); + PixelOperations.Instance.FromVector4Destructive(this.configuration, this.rgbaBuffer.GetSpan().Slice(0, destRow.Length), destRow); } } } diff --git a/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs index e814984633..d2a6fbf501 100644 --- a/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs @@ -120,7 +120,7 @@ namespace SixLabors.ImageSharp.PixelFormats this.BlendFunction(destinationSpan, backgroundSpan, sourceSpan, amount); Span sourceVectors = destinationSpan.Slice(0, background.Length); - PixelOperations.Instance.FromVector4(configuration, sourceVectors, destination, PixelConversionModifiers.Scale); + PixelOperations.Instance.FromVector4Destructive(configuration, sourceVectors, destination, PixelConversionModifiers.Scale); } } @@ -163,7 +163,7 @@ namespace SixLabors.ImageSharp.PixelFormats this.BlendFunction(destinationSpan, backgroundSpan, sourceSpan, amount); Span sourceVectors = destinationSpan.Slice(0, background.Length); - PixelOperations.Instance.FromVector4(configuration, sourceVectors, destination, PixelConversionModifiers.Scale); + PixelOperations.Instance.FromVector4Destructive(configuration, sourceVectors, destination, PixelConversionModifiers.Scale); } } } diff --git a/src/ImageSharp/PixelFormats/PixelConversionModifiers.cs b/src/ImageSharp/PixelFormats/PixelConversionModifiers.cs index d66dbb57a1..5df5dc62bc 100644 --- a/src/ImageSharp/PixelFormats/PixelConversionModifiers.cs +++ b/src/ImageSharp/PixelFormats/PixelConversionModifiers.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// Flags responsible to select additional operations which could be effitiently applied in /// /// or - /// + /// /// knowing the pixel type. /// [Flags] @@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.PixelFormats None = 0, /// - /// Select instead the standard (non scaled) variants. + /// Select and instead the standard (non scaled) variants. /// Scale = 1 << 0, diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.cs index c49f3f8343..4cff1f8c10 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.cs @@ -42,7 +42,7 @@ namespace SixLabors.ImageSharp.PixelFormats } /// - internal override void FromVector4(Configuration configuration, Span sourceVectors, Span destPixels, PixelConversionModifiers modifiers) + internal override void FromVector4Destructive(Configuration configuration, Span sourceVectors, Span destPixels, PixelConversionModifiers modifiers) { Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, modifiers.Remove(PixelConversionModifiers.Scale)); } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.cs index 92925898b6..225e4b5e01 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.cs @@ -42,7 +42,7 @@ namespace SixLabors.ImageSharp.PixelFormats } /// - internal override void FromVector4(Configuration configuration, Span sourceVectors, Span destPixels, PixelConversionModifiers modifiers) + internal override void FromVector4Destructive(Configuration configuration, Span sourceVectors, Span destPixels, PixelConversionModifiers modifiers) { Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, modifiers.Remove(PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply)); } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.cs index 73eddc235e..ce049115ea 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.cs @@ -42,7 +42,7 @@ namespace SixLabors.ImageSharp.PixelFormats } /// - internal override void FromVector4(Configuration configuration, Span sourceVectors, Span destPixels, PixelConversionModifiers modifiers) + internal override void FromVector4Destructive(Configuration configuration, Span sourceVectors, Span destPixels, PixelConversionModifiers modifiers) { Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, modifiers.Remove(PixelConversionModifiers.Scale)); } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs index 6de8c92bcf..02a8869ecc 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs @@ -42,7 +42,7 @@ namespace SixLabors.ImageSharp.PixelFormats } /// - internal override void FromVector4(Configuration configuration, Span sourceVectors, Span destPixels, PixelConversionModifiers modifiers) + internal override void FromVector4Destructive(Configuration configuration, Span sourceVectors, Span destPixels, PixelConversionModifiers modifiers) { Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, modifiers.Remove(PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply)); } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.PixelOperations.cs index aa9b82be7d..3da5d1855b 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.PixelOperations.cs @@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.PixelFormats } /// - internal override void FromVector4( + internal override void FromVector4Destructive( Configuration configuration, Span sourceVectors, Span destPixels, diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.PixelOperations.cs index 2d52f98cb6..3112126e76 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.PixelOperations.cs @@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.PixelFormats internal class PixelOperations : PixelOperations { /// - internal override void FromVector4( + internal override void FromVector4Destructive( Configuration configuration, Span sourceVectors, Span destinationColors, diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs index 6dae22c5ad..d009822e80 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs @@ -24,13 +24,17 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// Bulk version of converting 'sourceVectors.Length' pixels into 'destinationColors'. - /// TODO: Rename to DestructiveFromVector4() + add explain behavior + /// The method is DESTRUCTIVE altering the contents of . /// + /// + /// The destructive behavior is a design choice for performance reasons. + /// In a typical use case the contents of are abandoned after the conversion. + /// /// A to configure internal operations /// The to the source vectors. /// The to the destination colors. /// The to apply during the conversion - internal virtual void FromVector4( + internal virtual void FromVector4Destructive( Configuration configuration, Span sourceVectors, Span destPixels, @@ -43,13 +47,17 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// Bulk version of converting 'sourceVectors.Length' pixels into 'destinationColors'. - /// TODO: Rename to DestructiveFromVector4() + add explain behavior + /// The method is DESTRUCTIVE altering the contents of . /// + /// + /// The destructive behavior is a design choice for performance reasons. + /// In a typical use case the contents of are abandoned after the conversion. + /// /// A to configure internal operations /// The to the source vectors. /// The to the destination colors. - internal void FromVector4(Configuration configuration, Span sourceVectors, Span destPixels) => - this.FromVector4(configuration, sourceVectors, destPixels, PixelConversionModifiers.None); + internal void FromVector4Destructive(Configuration configuration, Span sourceVectors, Span destPixels) => + this.FromVector4Destructive(configuration, sourceVectors, destPixels, PixelConversionModifiers.None); /// /// Bulk version of converting 'sourceColors.Length' pixels into 'destinationVectors'. diff --git a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs index 5188a89a36..fe8d7dec3b 100644 --- a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs +++ b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs @@ -75,7 +75,7 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils } /// - /// Provides an efficient default implementation for + /// Provides an efficient default implementation for /// The method is works by internally converting to a therefore it's not applicable for that type! /// [MethodImpl(InliningOptions.ShortMethod)] diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs index bd1419e4bb..341a26ae84 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs @@ -82,7 +82,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution DenseMatrixUtils.Convolve2D(in matrixY, in matrixX, source.PixelBuffer, vectorSpan, y, x, maxY, maxX, startX); } - PixelOperations.Instance.FromVector4(configuration, vectorSpan.Slice(0, length), targetRowSpan); + PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan.Slice(0, length), targetRowSpan); } }); diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs index 05007c3706..9de4673289 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs @@ -100,7 +100,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution DenseMatrixUtils.Convolve(in matrix, sourcePixels, vectorSpan, y, x, maxY, maxX, startX); } - PixelOperations.Instance.FromVector4(configuration, vectorSpan.Slice(0, length), targetRowSpan); + PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan.Slice(0, length), targetRowSpan); } }); } diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs index 8ef64bdacc..7e003cb03e 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs @@ -66,7 +66,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution DenseMatrixUtils.Convolve(in matrix, source.PixelBuffer, vectorSpan, y, x, maxY, maxX, startX); } - PixelOperations.Instance.FromVector4(configuration, vectorSpan.Slice(0, length), targetRowSpan); + PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan.Slice(0, length), targetRowSpan); } }); diff --git a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs index d6a32d8899..dbd8433410 100644 --- a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs @@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters Vector4Utils.Transform(vectorSpan, ref matrix); - PixelOperations.Instance.FromVector4(configuration, vectorSpan, rowSpan); + PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan, rowSpan); } }); } diff --git a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs index 5a3b5943b7..0f71c732dd 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs @@ -129,7 +129,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms kernel.Convolve(point, x, ref ySpanRef, ref xSpanRef, source.PixelBuffer, vectorSpan); } - PixelOperations.Instance.FromVector4(configuration, vectorSpan, targetRowSpan); + PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan, targetRowSpan); } }); } diff --git a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs index 0b5627e19b..8a5d7563f4 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs @@ -128,7 +128,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms kernel.Convolve(point, x, ref ySpanRef, ref xSpanRef, source.PixelBuffer, vectorSpan); } - PixelOperations.Instance.FromVector4(configuration, vectorSpan, targetRowSpan); + PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan, targetRowSpan); } }); } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs index 6904122ccf..2c186a276b 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs @@ -301,7 +301,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms Span targetRowSpan = destination.GetPixelRowSpan(y); - PixelOperations.Instance.FromVector4(configuration, tempRowSpan, targetRowSpan, conversionModifiers); + PixelOperations.Instance.FromVector4Destructive(configuration, tempRowSpan, targetRowSpan, conversionModifiers); } }); } diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/FromVector4.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/FromVector4.cs index 2b9573ed73..8b2d08e66a 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/FromVector4.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/FromVector4.cs @@ -61,13 +61,13 @@ namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk [Benchmark] public void PixelOperations_Base() { - new PixelOperations().FromVector4(this.Configuration, this.source.GetSpan(), this.destination.GetSpan()); + new PixelOperations().FromVector4Destructive(this.Configuration, this.source.GetSpan(), this.destination.GetSpan()); } [Benchmark] public void PixelOperations_Specialized() { - PixelOperations.Instance.FromVector4(this.Configuration, this.source.GetSpan(), this.destination.GetSpan()); + PixelOperations.Instance.FromVector4Destructive(this.Configuration, this.source.GetSpan(), this.destination.GetSpan()); } } diff --git a/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs b/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs index cdd56fa074..ce4e16c446 100644 --- a/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs +++ b/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs @@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.Benchmarks destinationSpan[i] = PorterDuffFunctions.NormalSrcOver(backgroundSpan[i], sourceSpan[i], amount[i]); } - PixelOperations.Instance.FromVector4(this.Configuration, destinationSpan, destination); + PixelOperations.Instance.FromVector4Destructive(this.Configuration, destinationSpan, destination); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs index 659da47a7e..d9ae9131ff 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs @@ -112,7 +112,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations TestOperation( source, expected, - (s, d) => Operations.FromVector4(this.Configuration, s, d.GetSpan()) + (s, d) => Operations.FromVector4Destructive(this.Configuration, s, d.GetSpan()) ); } @@ -129,7 +129,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations (s, d) => { Span destPixels = d.GetSpan(); - Operations.FromVector4(this.Configuration, (Span)s, destPixels, PixelConversionModifiers.Scale); + Operations.FromVector4Destructive(this.Configuration, (Span)s, destPixels, PixelConversionModifiers.Scale); }); } @@ -153,7 +153,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations TestOperation( source, expected, - (s, d) => Operations.FromVector4( + (s, d) => Operations.FromVector4Destructive( this.Configuration, s, d.GetSpan(), @@ -187,7 +187,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations TestOperation( source, expected, - (s, d) => Operations.FromVector4(this.Configuration, s, d.GetSpan(), PixelConversionModifiers.Premultiply) + (s, d) => Operations.FromVector4Destructive(this.Configuration, s, d.GetSpan(), PixelConversionModifiers.Premultiply) ); } @@ -217,7 +217,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations TestOperation( source, expected, - (s, d) => Operations.FromVector4( + (s, d) => Operations.FromVector4Destructive( this.Configuration, s, d.GetSpan(), @@ -255,7 +255,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations TestOperation( source, expected, - (s, d) => Operations.FromVector4( + (s, d) => Operations.FromVector4Destructive( this.Configuration, s, d.GetSpan(), diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index ad31319bb9..6ca3ed6f9f 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Tests v.W = 1F; } - PixelOperations.Instance.FromVector4(configuration, tempSpan, pixelSpan, PixelConversionModifiers.Scale); + PixelOperations.Instance.FromVector4Destructive(configuration, tempSpan, pixelSpan, PixelConversionModifiers.Scale); } } }); From af0a6da3aecc9135ededf587a56ef9af5091b754 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 1 Apr 2019 02:20:38 +0200 Subject: [PATCH 044/223] pull back the submodule reference with a few revisions --- tests/Images/External | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Images/External b/tests/Images/External index 4387e56e85..9d71985545 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 4387e56e8590ee00607efe29a9babfda041621cb +Subproject commit 9d71985545bb827270ca34af3f78e4c220560ef1 From a834a7ce308a91e8282b9ea36028cd91c22bdb21 Mon Sep 17 00:00:00 2001 From: Jason Nelson Date: Mon, 1 Apr 2019 23:16:04 -0500 Subject: [PATCH 045/223] Nits (#870) * Format conversion models * Format color spaces * Add missing periods * Replace Endianness enum with isBigEndian * Seal DoubleBufferedStreamReader * Remove unused namespaces * Simplify variable names * Remove unused namespace * Format Block8x8F * Use MathF * Simplify exception when an encoder is not found * Format ICC models * Simplify excption messages in ExifValue (reduces IL size) * Add missing periods to documentation. * Format ImageProperty * Format BmpImageFormatDetector * Seal LocalFileSystem * Seal JpegColorConverter implementations * Add missing periods to jpeg documentation * Format ValueSize * Fix stylecop violation * Revert "Simplify excption messages in ExifValue (reduces IL size)" This reverts commit d22f2a679f4d9b1d5c3e41a56d1a7ea8e895f6c7. --- .../Advanced/AdvancedImageExtensions.cs | 6 +- src/ImageSharp/Advanced/IPixelSource.cs | 1 - src/ImageSharp/ColorSpaces/CieLch.cs | 6 +- src/ImageSharp/ColorSpaces/CieXyz.cs | 2 +- src/ImageSharp/ColorSpaces/Cmyk.cs | 2 +- .../ColorSpaces/Companding/SRgbCompanding.cs | 2 +- .../Conversion/ColorSpaceConverter.CieLab.cs | 47 +++++----- .../Conversion/ColorSpaceConverter.CieLch.cs | 48 +++++----- .../ColorSpaceConverter.CieLchuv.cs | 50 +++++----- .../Conversion/ColorSpaceConverter.CieLuv.cs | 54 +++++------ .../Conversion/ColorSpaceConverter.CieXyz.cs | 57 +++++------- .../Conversion/ColorSpaceConverter.Cmyk.cs | 42 ++++----- .../Conversion/ColorSpaceConverter.Hsl.cs | 36 ++++---- .../Conversion/ColorSpaceConverter.Hsv.cs | 2 +- .../ColorSpaceConverter.HunterLab.cs | 68 +++++++------- .../ColorSpaceConverter.LinearRgb.cs | 92 +++++++++---------- .../Conversion/ColorSpaceConverter.YCbCr.cs | 54 +++++------ .../Conversion/VonKriesChromaticAdaptation.cs | 4 +- src/ImageSharp/ColorSpaces/HunterLab.cs | 9 +- src/ImageSharp/ColorSpaces/Illuminants.cs | 6 +- src/ImageSharp/ColorSpaces/Rgb.cs | 8 +- .../ColorSpaces/RgbWorkingSpaces.cs | 3 +- .../Common/Extensions/EnumerableExtensions.cs | 4 +- src/ImageSharp/Common/Tuples/Octet.cs | 2 +- .../Formats/Bmp/BmpImageFormatDetector.cs | 8 +- .../Formats/Gif/IGifDecoderOptions.cs | 2 +- .../Jpeg/Components/Block8x8F.Generated.cs | 4 +- .../Jpeg/Components/Block8x8F.Generated.tt | 4 +- .../Formats/Jpeg/Components/Block8x8F.cs | 24 ++--- .../Jpeg/Components/Decoder/AdobeMarker.cs | 4 +- .../JpegColorConverter.FromCmyk.cs | 2 +- .../JpegColorConverter.FromGrayScale.cs | 2 +- .../JpegColorConverter.FromRgb.cs | 2 +- .../JpegColorConverter.FromYCbCrBasic.cs | 2 +- .../JpegColorConverter.FromYCbCrSimd.cs | 2 +- .../JpegColorConverter.FromYCbCrSimdAvx2.cs | 2 +- .../JpegColorConverter.FromYccK.cs | 2 +- .../ColorConverters/JpegColorConverter.cs | 4 +- .../Jpeg/Components/Decoder/FastACTable.cs | 2 +- .../Jpeg/Components/Decoder/HuffmanTable.cs | 8 +- .../Jpeg/Components/Decoder/JFifMarker.cs | 18 ++-- .../Decoder/JpegBlockPostProcessor.cs | 14 +-- .../Jpeg/Components/Decoder/JpegColorSpace.cs | 2 +- .../Jpeg/Components/Decoder/JpegComponent.cs | 2 +- .../Decoder/JpegComponentPostProcessor.cs | 2 +- .../Jpeg/Components/Decoder/JpegFrame.cs | 28 +++--- .../Components/Decoder/ProfileResolver.cs | 14 +-- src/ImageSharp/IImageInfo.cs | 2 +- .../IO/DoubleBufferedStreamReader.cs | 8 +- src/ImageSharp/IO/Endianness.cs | 21 ----- src/ImageSharp/IO/LocalFileSystem.cs | 2 +- src/ImageSharp/Image.FromBytes.cs | 15 ++- src/ImageSharp/Image.WrapMemory.cs | 47 +++++----- src/ImageSharp/ImageExtensions.Internal.cs | 1 - src/ImageSharp/ImageExtensions.cs | 8 +- src/ImageSharp/Memory/RowInterval.cs | 11 +-- src/ImageSharp/MetaData/ImageProperty.cs | 5 +- .../MetaData/Profiles/Exif/ExifReader.cs | 32 +++---- .../ICC/Various/IccColorantTableEntry.cs | 13 +-- .../ICC/Various/IccLocalizedString.cs | 5 +- .../Profiles/ICC/Various/IccProfileId.cs | 25 ++--- .../ICC/Various/IccScreeningChannel.cs | 9 +- .../Profiles/ICC/Various/IccTagTableEntry.cs | 21 ++--- .../PixelImplementations/NormalizedByte4.cs | 8 +- src/ImageSharp/Primitives/LongRational.cs | 2 +- src/ImageSharp/Primitives/ValueSize.cs | 22 ++--- 66 files changed, 465 insertions(+), 551 deletions(-) delete mode 100644 src/ImageSharp/IO/Endianness.cs diff --git a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs index 63ccea4e66..bdcb4c10f1 100644 --- a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs +++ b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Advanced /// Gets the configuration for the image. /// /// The Pixel format. - /// The source image + /// The source image. /// Returns the configuration. public static Configuration GetConfiguration(this Image source) where TPixel : struct, IPixel @@ -150,7 +150,7 @@ namespace SixLabors.ImageSharp.Advanced /// /// Gets the assigned to 'source'. /// - /// The source image + /// The source image. /// Returns the configuration. internal static MemoryAllocator GetMemoryAllocator(this IConfigurable source) => GetConfiguration(source).MemoryAllocator; @@ -185,7 +185,7 @@ namespace SixLabors.ImageSharp.Advanced /// The source. /// The row. /// - /// The span returned from Pixel source + /// The span returned from Pixel source. /// private static Span GetSpan(Buffer2D source, int row) where TPixel : struct, IPixel diff --git a/src/ImageSharp/Advanced/IPixelSource.cs b/src/ImageSharp/Advanced/IPixelSource.cs index 19616d7427..a321e877ba 100644 --- a/src/ImageSharp/Advanced/IPixelSource.cs +++ b/src/ImageSharp/Advanced/IPixelSource.cs @@ -3,7 +3,6 @@ using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; namespace SixLabors.ImageSharp.Advanced { diff --git a/src/ImageSharp/ColorSpaces/CieLch.cs b/src/ImageSharp/ColorSpaces/CieLch.cs index 074bc1516b..99d4c09e97 100644 --- a/src/ImageSharp/ColorSpaces/CieLch.cs +++ b/src/ImageSharp/ColorSpaces/CieLch.cs @@ -13,15 +13,15 @@ namespace SixLabors.ImageSharp.ColorSpaces /// public readonly struct CieLch : IEquatable { - private static readonly Vector3 Min = new Vector3(0, -200, 0); - private static readonly Vector3 Max = new Vector3(100, 200, 360); - /// /// D50 standard illuminant. /// Used when reference white is not specified explicitly. /// public static readonly CieXyz DefaultWhitePoint = Illuminants.D50; + private static readonly Vector3 Min = new Vector3(0, -200, 0); + private static readonly Vector3 Max = new Vector3(100, 200, 360); + /// /// Gets the lightness dimension. /// A value ranging between 0 (black), 100 (diffuse white) or higher (specular white). diff --git a/src/ImageSharp/ColorSpaces/CieXyz.cs b/src/ImageSharp/ColorSpaces/CieXyz.cs index 30521476e5..6c5adcb219 100644 --- a/src/ImageSharp/ColorSpaces/CieXyz.cs +++ b/src/ImageSharp/ColorSpaces/CieXyz.cs @@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp.ColorSpaces public readonly float Y; /// - /// Gets the Z component. Quasi-equal to blue stimulation, or the S cone response + /// Gets the Z component. Quasi-equal to blue stimulation, or the S cone response. /// A value usually ranging between 0 and 1. /// public readonly float Z; diff --git a/src/ImageSharp/ColorSpaces/Cmyk.cs b/src/ImageSharp/ColorSpaces/Cmyk.cs index 04901126c1..c2331c3798 100644 --- a/src/ImageSharp/ColorSpaces/Cmyk.cs +++ b/src/ImageSharp/ColorSpaces/Cmyk.cs @@ -78,7 +78,7 @@ namespace SixLabors.ImageSharp.ColorSpaces public static bool operator ==(Cmyk left, Cmyk right) => left.Equals(right); /// - /// Compares two objects for inequality + /// Compares two objects for inequality. /// /// The on the left side of the operand. /// The on the right side of the operand. diff --git a/src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs b/src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs index bbd454fbe8..cc30c3632d 100644 --- a/src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs +++ b/src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs @@ -9,7 +9,7 @@ using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.ColorSpaces.Companding { /// - /// Implements sRGB companding + /// Implements sRGB companding. /// /// /// For more info see: diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs index 3c197673d3..bfabbb21d5 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion private static readonly CieLchToCieLabConverter CieLchToCieLabConverter = new CieLchToCieLabConverter(); /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -28,12 +28,11 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion // Conversion (perserving white point) CieLab unadapted = CieLchToCieLabConverter.Convert(color); - // Adaptation return this.Adapt(unadapted); } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -54,7 +53,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -66,7 +65,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -87,7 +86,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -99,7 +98,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -120,7 +119,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -132,7 +131,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -159,10 +158,8 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// The public CieLab ToCieLab(in CieXyz color) { - // Adaptation CieXyz adapted = this.Adapt(color, this.whitePoint, this.targetLabWhitePoint); - // Conversion return this.cieXyzToCieLabConverter.Convert(adapted); } @@ -199,7 +196,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -220,7 +217,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -232,7 +229,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -253,7 +250,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -264,7 +261,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -285,7 +282,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -297,7 +294,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -318,7 +315,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -330,7 +327,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -351,7 +348,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -363,7 +360,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -384,7 +381,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -396,7 +393,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -429,7 +426,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs index 0a8607e3bc..3810736197 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs @@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -55,7 +55,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -67,7 +67,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -88,7 +88,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -100,7 +100,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -121,7 +121,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -133,7 +133,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -166,7 +166,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -187,7 +187,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -198,7 +198,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -219,7 +219,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -231,7 +231,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -252,7 +252,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -264,7 +264,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -285,7 +285,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -297,7 +297,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -318,7 +318,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -330,7 +330,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -351,7 +351,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -363,7 +363,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -384,7 +384,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -396,7 +396,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -417,7 +417,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -429,7 +429,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs index 3a779ee722..6ca40af035 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs @@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -86,21 +86,19 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The public CieLchuv ToCieLchuv(in CieLuv color) { - // Adaptation CieLuv adapted = this.Adapt(color); - // Conversion return CieLuvToCieLchuvConverter.Convert(adapted); } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -121,7 +119,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -133,7 +131,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -154,7 +152,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -166,7 +164,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -187,7 +185,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -199,7 +197,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -220,7 +218,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -253,7 +251,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -265,7 +263,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -286,7 +284,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -298,7 +296,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -319,7 +317,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -331,7 +329,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -352,7 +350,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -364,7 +362,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -385,7 +383,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -397,7 +395,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -418,7 +416,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -429,7 +427,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs index 90eb8e34d7..316b35f37a 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs @@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion private static readonly CieLchuvToCieLuvConverter CieLchuvToCieLuvConverter = new CieLchuvToCieLuvConverter(); /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -60,7 +60,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -81,7 +81,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -95,7 +95,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -116,7 +116,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -127,7 +127,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -148,7 +148,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -162,7 +162,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -183,7 +183,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -194,7 +194,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -226,7 +226,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -247,7 +247,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -258,7 +258,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -279,7 +279,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -290,7 +290,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -311,7 +311,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -322,7 +322,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -343,7 +343,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -354,7 +354,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -375,7 +375,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -386,7 +386,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -407,7 +407,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -418,10 +418,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// - /// The span to the source colors - /// The span to the destination colors + /// The span to the source colors. + /// The span to the destination colors. public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs index fada6d9c59..2cc8437bba 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion private LinearRgbToCieXyzConverter linearRgbToCieXyzConverter; /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -73,7 +73,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -94,7 +94,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -108,7 +108,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -143,7 +143,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -164,7 +164,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -175,7 +175,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -196,20 +196,19 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The public CieXyz ToCieXyz(in Cmyk color) { - // Conversion var rgb = this.ToRgb(color); return this.ToCieXyz(rgb); } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -230,23 +229,22 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The public CieXyz ToCieXyz(in Hsl color) { - // Conversion var rgb = this.ToRgb(color); return this.ToCieXyz(rgb); } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// - /// The span to the source colors - /// The span to the destination colors + /// The span to the source colors. + /// The span to the destination colors. public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); @@ -264,7 +262,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -277,7 +275,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -298,21 +296,19 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The public CieXyz ToCieXyz(in HunterLab color) { - // Conversion CieXyz unadapted = HunterLabToCieXyzConverter.Convert(color); - // Adaptation return this.Adapt(unadapted, color.WhitePoint); } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -333,7 +329,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -343,15 +339,14 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion LinearRgbToCieXyzConverter converter = this.GetLinearRgbToCieXyzConverter(color.WorkingSpace); CieXyz unadapted = converter.Convert(color); - // Adaptation return this.Adapt(unadapted, color.WorkingSpace.WhitePoint); } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// - /// The span to the source colors - /// The span to the destination colors + /// The span to the source colors. + /// The span to the destination colors. public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); @@ -369,18 +364,17 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The public CieXyz ToCieXyz(in Lms color) { - // Conversion return this.cieXyzAndLmsConverter.Convert(color); } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -440,7 +434,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// The public CieXyz ToCieXyz(in YCbCr color) { - // Conversion var rgb = this.ToRgb(color); return this.ToCieXyz(rgb); diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs index b798516359..00e20e25b4 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs @@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion private static readonly CmykAndRgbConverter CmykAndRgbConverter = new CmykAndRgbConverter(); /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -62,7 +62,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -149,7 +149,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -161,7 +161,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -182,7 +182,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -215,7 +215,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -227,7 +227,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -248,7 +248,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -260,7 +260,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -281,7 +281,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -293,7 +293,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -314,7 +314,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -326,7 +326,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -347,7 +347,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -359,9 +359,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// - /// The span to the source colors + /// The span to the source colors, /// The span to the destination colors public void Convert(ReadOnlySpan source, Span destination) { @@ -380,7 +380,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -408,7 +408,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -420,7 +420,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs index a7080b9749..76175f1cba 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs @@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion private static readonly HslAndRgbConverter HslAndRgbConverter = new HslAndRgbConverter(); /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -62,7 +62,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -83,7 +83,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -95,7 +95,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -116,7 +116,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -128,7 +128,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -149,7 +149,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -281,7 +281,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -293,7 +293,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -314,7 +314,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -326,7 +326,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -347,7 +347,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -359,7 +359,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -387,7 +387,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion public Hsl ToHsl(in Rgb color) => HslAndRgbConverter.Convert(color); /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -408,7 +408,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -420,7 +420,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs index a2121203c1..e64beb3e49 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs @@ -263,7 +263,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// Performs the bulk conversion from into /// /// The span to the source colors - /// The span to the destination colors + /// The span to the destination colors. public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs index e5996c238e..91e5549ac8 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs @@ -13,10 +13,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion public partial class ColorSpaceConverter { /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// - /// The span to the source colors - /// The span to the destination colors + /// The span to the source colors. + /// The span to the destination colors. public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); @@ -34,10 +34,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// - /// The span to the source colors - /// The span to the destination colors + /// The span to the source colors. + /// The span to the destination colors. public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); @@ -55,7 +55,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -76,7 +76,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -97,10 +97,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// - /// The span to the source colors - /// The span to the destination colors + /// The span to the source colors. + /// The span to the destination colors. public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); @@ -118,10 +118,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// - /// The span to the source colors - /// The span to the destination colors + /// The span to the source colors. + /// The span to the destination colors. public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); @@ -139,7 +139,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -160,10 +160,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors - /// The span to the destination colors + /// The span to the destination colors. public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); @@ -181,7 +181,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -223,7 +223,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -244,7 +244,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -265,7 +265,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -286,7 +286,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -297,7 +297,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -308,7 +308,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -319,7 +319,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -330,7 +330,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -341,21 +341,19 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The public HunterLab ToHunterLab(in CieXyz color) { - // Adaptation CieXyz adapted = this.Adapt(color, this.whitePoint, this.targetHunterLabWhitePoint); - // Conversion return this.cieXyzToHunterLabConverter.Convert(adapted); } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -366,7 +364,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -377,7 +375,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -388,7 +386,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -399,7 +397,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -421,7 +419,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs index eef626be2f..4be3f0079d 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs @@ -17,10 +17,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion private static readonly RgbToLinearRgbConverter RgbToLinearRgbConverter = new RgbToLinearRgbConverter(); /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// - /// The span to the source colors - /// The span to the destination colors + /// The span to the source colors. + /// The span to the destination colors. public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); @@ -38,10 +38,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// - /// The span to the source colors - /// The span to the destination colors + /// The span to the source colors. + /// The span to the destination colors. public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); @@ -59,10 +59,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// - /// The span to the source colors - /// The span to the destination colors + /// The span to the source colors. + /// The span to the destination colors. public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); @@ -80,10 +80,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// - /// The span to the source colors - /// The span to the destination colors + /// The span to the source colors. + /// The span to the destination colors. public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); @@ -101,10 +101,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// - /// The span to the source colors - /// The span to the destination colors + /// The span to the source colors. + /// The span to the destination colors. public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); @@ -122,10 +122,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// - /// The span to the source colors - /// The span to the destination colors + /// The span to the source colors. + /// The span to the destination colors. public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); @@ -143,10 +143,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// - /// The span to the source colors - /// The span to the destination colors + /// The span to the source colors. + /// The span to the destination colors. public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); @@ -164,10 +164,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// - /// The span to the source colors - /// The span to the destination colors + /// The span to the source colors. + /// The span to the destination colors. public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); @@ -185,10 +185,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// - /// The span to the source colors - /// The span to the destination colors + /// The span to the source colors. + /// The span to the destination colors. public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); @@ -206,10 +206,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// - /// The span to the source colors - /// The span to the destination colors + /// The span to the source colors. + /// The span to the destination colors. public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); @@ -227,10 +227,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// - /// The span to the source colors - /// The span to the destination colors + /// The span to the source colors. + /// The span to the destination colors. public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); @@ -248,7 +248,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -269,7 +269,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -290,7 +290,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -301,7 +301,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -312,7 +312,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -323,7 +323,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -345,7 +345,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -359,7 +359,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -370,7 +370,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -381,7 +381,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -392,7 +392,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -403,7 +403,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -425,7 +425,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs index 5780f4f545..68cd34bf2e 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs @@ -17,10 +17,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion private static readonly YCbCrAndRgbConverter YCbCrAndRgbConverter = new YCbCrAndRgbConverter(); /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// - /// The span to the source colors - /// The span to the destination colors + /// The span to the source colors. + /// The span to the destination colors. public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); @@ -38,10 +38,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// - /// The span to the source colors - /// The span to the destination colors + /// The span to the source colors. + /// The span to the destination colors. public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); @@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -101,7 +101,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -122,7 +122,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -143,7 +143,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -164,7 +164,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -185,7 +185,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -206,7 +206,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -227,7 +227,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -248,7 +248,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Performs the bulk conversion from into + /// Performs the bulk conversion from into . /// /// The span to the source colors /// The span to the destination colors @@ -269,7 +269,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -281,7 +281,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -293,7 +293,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -305,7 +305,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -317,7 +317,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -329,7 +329,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -341,7 +341,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -353,7 +353,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -365,7 +365,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -377,7 +377,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -389,7 +389,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The @@ -401,7 +401,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a . /// /// The color to convert. /// The diff --git a/src/ImageSharp/ColorSpaces/Conversion/VonKriesChromaticAdaptation.cs b/src/ImageSharp/ColorSpaces/Conversion/VonKriesChromaticAdaptation.cs index 85a36331b2..a4d96d19e7 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/VonKriesChromaticAdaptation.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/VonKriesChromaticAdaptation.cs @@ -10,13 +10,13 @@ using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// - /// Basic implementation of the von Kries chromatic adaptation model + /// Implementation of the von Kries chromatic adaptation model. /// /// /// Transformation described here: /// http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html /// - public class VonKriesChromaticAdaptation : IChromaticAdaptation + public sealed class VonKriesChromaticAdaptation : IChromaticAdaptation { private readonly CieXyzAndLmsConverter converter; diff --git a/src/ImageSharp/ColorSpaces/HunterLab.cs b/src/ImageSharp/ColorSpaces/HunterLab.cs index dcae65e425..402761d8c1 100644 --- a/src/ImageSharp/ColorSpaces/HunterLab.cs +++ b/src/ImageSharp/ColorSpaces/HunterLab.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.ColorSpaces { /// /// Represents an Hunter LAB color. - /// + /// . /// public readonly struct HunterLab : IEquatable { @@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp.ColorSpaces public readonly float B; /// - /// Gets the reference white point of this color + /// Gets the reference white point of this color. /// public readonly CieXyz WhitePoint; @@ -117,10 +117,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// [MethodImpl(InliningOptions.ShortMethod)] - public override int GetHashCode() - { - return HashCode.Combine(this.L, this.A, this.B, this.WhitePoint); - } + public override int GetHashCode() => HashCode.Combine(this.L, this.A, this.B, this.WhitePoint); /// public override string ToString() => FormattableString.Invariant($"HunterLab({this.L:#0.##}, {this.A:#0.##}, {this.B:#0.##})"); diff --git a/src/ImageSharp/ColorSpaces/Illuminants.cs b/src/ImageSharp/ColorSpaces/Illuminants.cs index ed385e02cd..d5c1b3eed5 100644 --- a/src/ImageSharp/ColorSpaces/Illuminants.cs +++ b/src/ImageSharp/ColorSpaces/Illuminants.cs @@ -8,11 +8,9 @@ namespace SixLabors.ImageSharp.ColorSpaces /// Standard illuminants provide a basis for comparing images or colors recorded under different lighting /// /// - /// Coefficients taken from: - /// http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html + /// Coefficients taken from: http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html ///
- /// Descriptions taken from: - /// http://en.wikipedia.org/wiki/Standard_illuminant + /// Descriptions taken from: http://en.wikipedia.org/wiki/Standard_illuminant ///
public static class Illuminants { diff --git a/src/ImageSharp/ColorSpaces/Rgb.cs b/src/ImageSharp/ColorSpaces/Rgb.cs index cdc9e90ad4..5cb89a2931 100644 --- a/src/ImageSharp/ColorSpaces/Rgb.cs +++ b/src/ImageSharp/ColorSpaces/Rgb.cs @@ -14,14 +14,14 @@ namespace SixLabors.ImageSharp.ColorSpaces ///
public readonly struct Rgb : IEquatable { - private static readonly Vector3 Min = Vector3.Zero; - private static readonly Vector3 Max = Vector3.One; - /// - /// The default rgb working space + /// The default rgb working space. /// public static readonly RgbWorkingSpaceBase DefaultWorkingSpace = RgbWorkingSpaces.SRgb; + private static readonly Vector3 Min = Vector3.Zero; + private static readonly Vector3 Max = Vector3.One; + /// /// Gets the red component. /// A value usually ranging between 0 and 1. diff --git a/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs b/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs index ee3822c152..7ddcae7cea 100644 --- a/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs +++ b/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs @@ -8,8 +8,7 @@ using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; namespace SixLabors.ImageSharp.ColorSpaces { /// - /// Chromaticity coordinates taken from: - /// + /// Chromaticity coordinates based on: /// public static class RgbWorkingSpaces { diff --git a/src/ImageSharp/Common/Extensions/EnumerableExtensions.cs b/src/ImageSharp/Common/Extensions/EnumerableExtensions.cs index e97a2eaed0..e28db7cff1 100644 --- a/src/ImageSharp/Common/Extensions/EnumerableExtensions.cs +++ b/src/ImageSharp/Common/Extensions/EnumerableExtensions.cs @@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.Common /// The start index, inclusive. /// /// - /// A method that has one parameter and returns a calculating the end index + /// A method that has one parameter and returns a calculating the end index. /// /// /// The incremental step. @@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.Common /// The start index, inclusive. /// /// - /// A method that has one parameter and returns a calculating the end index + /// A method that has one parameter and returns a calculating the end index. /// /// /// The incremental step. diff --git a/src/ImageSharp/Common/Tuples/Octet.cs b/src/ImageSharp/Common/Tuples/Octet.cs index 23faf08ae9..7ece2ed562 100644 --- a/src/ImageSharp/Common/Tuples/Octet.cs +++ b/src/ImageSharp/Common/Tuples/Octet.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Tuples internal static class Octet { /// - /// Value tuple of -s + /// Value tuple of -s. /// [StructLayout(LayoutKind.Explicit, Size = 8 * sizeof(uint))] public struct OfUInt32 diff --git a/src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs b/src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs index 6a740d47d1..c0814b1dfc 100644 --- a/src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs +++ b/src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs @@ -6,7 +6,7 @@ using System; namespace SixLabors.ImageSharp.Formats.Bmp { /// - /// Detects bmp file headers + /// Detects bmp file headers. /// public sealed class BmpImageFormatDetector : IImageFormatDetector { @@ -22,9 +22,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp private bool IsSupportedFileFormat(ReadOnlySpan header) { // TODO: This should be in constants - return header.Length >= this.HeaderSize && - header[0] == 0x42 && // B - header[1] == 0x4D; // M + return header.Length >= this.HeaderSize + && header[0] == 0x42 // B + && header[1] == 0x4D; // M } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs b/src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs index 42c202a3d9..0c59163cce 100644 --- a/src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs +++ b/src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs @@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Formats.Gif Encoding TextEncoding { get; } /// - /// Gets the decoding mode for multi-frame images + /// Gets the decoding mode for multi-frame images. /// FrameDecodingMode DecodingMode { get; } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs index 1f47de594c..a9e9903a9d 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs @@ -97,7 +97,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components { Vector4 CMin4 = new Vector4(0F); Vector4 CMax4 = new Vector4(maximum); - Vector4 COff4 = new Vector4((float)Math.Ceiling(maximum/2)); + Vector4 COff4 = new Vector4(MathF.Ceiling(maximum / 2)); this.V0L = Vector4.Clamp(this.V0L + COff4, CMin4, CMax4); this.V0R = Vector4.Clamp(this.V0R + COff4, CMin4, CMax4); @@ -123,7 +123,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components [MethodImpl(InliningOptions.ShortMethod)] public void NormalizeColorsAndRoundInplaceAvx2(float maximum) { - Vector off = new Vector((float)Math.Ceiling(maximum/2)); + Vector off = new Vector(MathF.Ceiling(maximum / 2)); Vector max = new Vector(maximum); ref Vector row0 = ref Unsafe.As>(ref this.V0L); diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt index ec4e06e429..d6c42a802a 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt @@ -62,7 +62,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components { Vector4 CMin4 = new Vector4(0F); Vector4 CMax4 = new Vector4(maximum); - Vector4 COff4 = new Vector4((float)Math.Ceiling(maximum/2)); + Vector4 COff4 = new Vector4(MathF.Ceiling(maximum / 2)); <# @@ -86,7 +86,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components [MethodImpl(InliningOptions.ShortMethod)] public void NormalizeColorsAndRoundInplaceAvx2(float maximum) { - Vector off = new Vector((float)Math.Ceiling(maximum/2)); + Vector off = new Vector(MathF.Ceiling(maximum / 2)); Vector max = new Vector(maximum); <# diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs index c0de450695..c85bd725c9 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs @@ -147,7 +147,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components } /// - /// Fill the block with defaults (zeroes) + /// Fill the block with defaults (zeroes). /// [MethodImpl(InliningOptions.ShortMethod)] public void Clear() @@ -157,7 +157,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components } /// - /// Load raw 32bit floating point data from source + /// Load raw 32bit floating point data from source. /// /// Source [MethodImpl(InliningOptions.ShortMethod)] @@ -170,7 +170,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components } /// - /// Load raw 32bit floating point data from source + /// Load raw 32bit floating point data from source. /// /// Block pointer /// Source @@ -197,7 +197,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components } /// - /// Copy raw 32bit floating point data to dest + /// Copy raw 32bit floating point data to dest, /// /// Destination [MethodImpl(InliningOptions.ShortMethod)] @@ -210,7 +210,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components } /// - /// Convert salars to byte-s and copy to dest + /// Convert salars to byte-s and copy to dest, /// /// Pointer to block /// Destination @@ -226,10 +226,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components } /// - /// Copy raw 32bit floating point data to dest + /// Copy raw 32bit floating point data to dest. /// - /// Block pointer - /// Destination + /// The block pointer. + /// The destination. [MethodImpl(InliningOptions.ShortMethod)] public static unsafe void CopyTo(Block8x8F* blockPtr, Span dest) { @@ -275,7 +275,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components /// /// Multiply all elements of the block. /// - /// The value to multiply by + /// The value to multiply by. [MethodImpl(InliningOptions.ShortMethod)] public void MultiplyInplace(float value) { @@ -298,7 +298,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components } /// - /// Multiply all elements of the block by the corresponding elements of 'other' + /// Multiply all elements of the block by the corresponding elements of 'other'. /// [MethodImpl(InliningOptions.ShortMethod)] public void MultiplyInplace(ref Block8x8F other) @@ -349,8 +349,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components /// /// Quantize the block. /// - /// Block pointer - /// Qt pointer + /// The block pointer. + /// The qt pointer. /// Unzig pointer // [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void DequantizeBlock(Block8x8F* blockPtr, Block8x8F* qtPtr, byte* unzigPtr) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/AdobeMarker.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/AdobeMarker.cs index 43de39ac78..e34af98251 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/AdobeMarker.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/AdobeMarker.cs @@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// /// Gets the horizontal downsampling hint used for DCT encoding /// 0x0 : (none - Chop) - /// Bit 15 : Encoded with Blend=1 downsampling + /// Bit 15 : Encoded with Blend=1 downsampling. /// public short APP14Flags0 { get; } @@ -63,7 +63,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// Converts the specified byte array representation of an Adobe marker to its equivalent and /// returns a value that indicates whether the conversion succeeded. /// - /// The byte array containing metadata to parse + /// The byte array containing metadata to parse. /// The marker to return. public static bool TryParse(byte[] bytes, out AdobeMarker marker) { diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmyk.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmyk.cs index d4dc31fe0c..a57535b71c 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmyk.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmyk.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters { internal abstract partial class JpegColorConverter { - internal class FromCmyk : JpegColorConverter + internal sealed class FromCmyk : JpegColorConverter { public FromCmyk(int precision) : base(JpegColorSpace.Cmyk, precision) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs index 4a5dfa6322..15bb2cf4b4 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs @@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters { internal abstract partial class JpegColorConverter { - internal class FromGrayscale : JpegColorConverter + internal sealed class FromGrayscale : JpegColorConverter { public FromGrayscale(int precision) : base(JpegColorSpace.Grayscale, precision) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgb.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgb.cs index 516dfb39fe..2f68e312dc 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgb.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgb.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters { internal abstract partial class JpegColorConverter { - internal class FromRgb : JpegColorConverter + internal sealed class FromRgb : JpegColorConverter { public FromRgb(int precision) : base(JpegColorSpace.RGB, precision) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs index 124aac1224..a646cd6cfe 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters { internal abstract partial class JpegColorConverter { - internal class FromYCbCrBasic : JpegColorConverter + internal sealed class FromYCbCrBasic : JpegColorConverter { public FromYCbCrBasic(int precision) : base(JpegColorSpace.YCbCr, precision) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs index 10ef02a931..1706b4c1bc 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters { internal abstract partial class JpegColorConverter { - internal class FromYCbCrSimd : JpegColorConverter + internal sealed class FromYCbCrSimd : JpegColorConverter { public FromYCbCrSimd(int precision) : base(JpegColorSpace.YCbCr, precision) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs index 9953f78c18..093ea2f9a2 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters { internal abstract partial class JpegColorConverter { - internal class FromYCbCrSimdAvx2 : JpegColorConverter + internal sealed class FromYCbCrSimdAvx2 : JpegColorConverter { public FromYCbCrSimdAvx2(int precision) : base(JpegColorSpace.YCbCr, precision) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccK.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccK.cs index 94be11e237..cd8a3bb06c 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccK.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccK.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters { internal abstract partial class JpegColorConverter { - internal class FromYccK : JpegColorConverter + internal sealed class FromYccK : JpegColorConverter { public FromYccK(int precision) : base(JpegColorSpace.Ycck, precision) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs index c2e390c590..61e3598696 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs @@ -42,8 +42,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters { this.ColorSpace = colorSpace; this.Precision = precision; - this.MaximumValue = (float)Math.Pow(2, precision) - 1; - this.HalfValue = (float)Math.Ceiling(this.MaximumValue / 2); + this.MaximumValue = MathF.Pow(2, precision) - 1; + this.HalfValue = MathF.Ceiling(this.MaximumValue / 2); } /// diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTable.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTable.cs index 57d2221ba3..dc78a89ddb 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTable.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTable.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder internal unsafe struct FastACTable { /// - /// Gets the lookahead array + /// Gets the lookahead array. /// public fixed short Lookahead[512]; diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs index a2c9bade19..7e025e271e 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs @@ -26,22 +26,22 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder #pragma warning restore IDE0044 // Add readonly modifier /// - /// Gets the max code array + /// Gets the max code array. /// public fixed uint MaxCode[18]; /// - /// Gets the value offset array + /// Gets the value offset array. /// public fixed int ValOffset[18]; /// - /// Gets the huffman value array + /// Gets the huffman value array. /// public fixed byte Values[256]; /// - /// Gets the lookahead array + /// Gets the lookahead array. /// public fixed byte Lookahead[512]; diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs index 76f0e2cb51..5bd5920dbe 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs @@ -7,7 +7,7 @@ using SixLabors.ImageSharp.MetaData; namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { /// - /// Provides information about the JFIF marker segment + /// Provides information about the JFIF marker segment. /// TODO: Thumbnail? /// internal readonly struct JFifMarker : IEquatable @@ -20,11 +20,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// /// Initializes a new instance of the struct. /// - /// The major version - /// The minor version - /// The units for the density values - /// The horizontal pixel density - /// The vertical pixel density + /// The major version. + /// The minor version. + /// The units for the density values. + /// The horizontal pixel density. + /// The vertical pixel density. private JFifMarker(byte majorVersion, byte minorVersion, byte densityUnits, short xDensity, short yDensity) { if (xDensity <= 0) @@ -47,12 +47,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder } /// - /// Gets the major version + /// Gets the major version. /// public byte MajorVersion { get; } /// - /// Gets the minor version + /// Gets the minor version. /// public byte MinorVersion { get; } @@ -78,7 +78,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// Converts the specified byte array representation of an JFIF marker to its equivalent and /// returns a value that indicates whether the conversion succeeded. /// - /// The byte array containing metadata to parse + /// The byte array containing metadata to parse. /// The marker to return. public static bool TryParse(byte[] bytes, out JFifMarker marker) { diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs index fe39f41884..c5efb812e0 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs @@ -20,17 +20,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder public Block8x8F SourceBlock; /// - /// Temporal block 1 to store intermediate and/or final computation results + /// Temporal block 1 to store intermediate and/or final computation results. /// public Block8x8F WorkspaceBlock1; /// - /// Temporal block 2 to store intermediate and/or final computation results + /// Temporal block 2 to store intermediate and/or final computation results. /// public Block8x8F WorkspaceBlock2; /// - /// The quantization table as + /// The quantization table as . /// public Block8x8F DequantiazationTable; @@ -40,9 +40,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder private Size subSamplingDivisors; /// - /// Defines the maximum value derived from the bitdepth + /// Defines the maximum value derived from the bitdepth. /// - private int maximumValue; + private readonly int maximumValue; /// /// Initializes a new instance of the struct. @@ -54,7 +54,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder int qtIndex = component.QuantizationTableIndex; this.DequantiazationTable = ZigZag.CreateDequantizationTable(ref decoder.QuantizationTables[qtIndex]); this.subSamplingDivisors = component.SubSamplingDivisors; - this.maximumValue = (int)Math.Pow(2, decoder.Precision) - 1; + this.maximumValue = (int)MathF.Pow(2, decoder.Precision) - 1; this.SourceBlock = default; this.WorkspaceBlock1 = default; @@ -66,7 +66,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// - Dequantize /// - Applying IDCT /// - Level shift by +maximumValue/2, clip to [0, maximumValue] - /// - Copy the resulting color values into 'destArea' scaling up the block by amount defined in + /// - Copy the resulting color values into 'destArea' scaling up the block by amount defined in . /// /// The source block. /// The destination buffer area. diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegColorSpace.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegColorSpace.cs index 2861a2c2e9..aa33744adf 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegColorSpace.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegColorSpace.cs @@ -4,7 +4,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { /// - /// Identifies the colorspace of a Jpeg image + /// Identifies the colorspace of a Jpeg image. /// internal enum JpegColorSpace { diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs index ac24f4ca58..5353303947 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs @@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder } /// - /// Gets the component Id. + /// Gets the component id. /// public byte Id { get; } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs index e7f3e4fda5..e1a9380a03 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs @@ -78,7 +78,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder public void CopyBlocksToColorBuffer() { var blockPp = new JpegBlockPostProcessor(this.ImagePostProcessor.RawJpeg, this.Component); - float maximumValue = (float)Math.Pow(2, this.ImagePostProcessor.RawJpeg.Precision) - 1; + float maximumValue = MathF.Pow(2, this.ImagePostProcessor.RawJpeg.Precision) - 1; for (int y = 0; y < this.BlockRowsPerStep; y++) { diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs index 33a7c68baf..f426eb1b15 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs @@ -11,68 +11,68 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder internal sealed class JpegFrame : IDisposable { /// - /// Gets or sets a value indicating whether the frame uses the extended specification + /// Gets or sets a value indicating whether the frame uses the extended specification. /// public bool Extended { get; set; } /// - /// Gets or sets a value indicating whether the frame uses the progressive specification + /// Gets or sets a value indicating whether the frame uses the progressive specification. /// public bool Progressive { get; set; } /// - /// Gets or sets the precision + /// Gets or sets the precision. /// public byte Precision { get; set; } /// - /// Gets or sets the number of scanlines within the frame + /// Gets or sets the number of scanlines within the frame. /// public short Scanlines { get; set; } /// - /// Gets or sets the number of samples per scanline + /// Gets or sets the number of samples per scanline. /// public short SamplesPerLine { get; set; } /// - /// Gets or sets the number of components within a frame. In progressive frames this value can range from only 1 to 4 + /// Gets or sets the number of components within a frame. In progressive frames this value can range from only 1 to 4. /// public byte ComponentCount { get; set; } /// - /// Gets or sets the component id collection + /// Gets or sets the component id collection. /// public byte[] ComponentIds { get; set; } /// - /// Gets or sets the order in which to process the components + /// Gets or sets the order in which to process the components. /// in interleaved mode. /// public byte[] ComponentOrder { get; set; } /// - /// Gets or sets the frame component collection + /// Gets or sets the frame component collection. /// public JpegComponent[] Components { get; set; } /// - /// Gets or sets the maximum horizontal sampling factor + /// Gets or sets the maximum horizontal sampling factor. /// public int MaxHorizontalFactor { get; set; } /// - /// Gets or sets the maximum vertical sampling factor + /// Gets or sets the maximum vertical sampling factor. /// public int MaxVerticalFactor { get; set; } /// - /// Gets or sets the number of MCU's per line + /// Gets or sets the number of MCU's per line. /// public int McusPerLine { get; set; } /// - /// Gets or sets the number of MCU's per column + /// Gets or sets the number of MCU's per column. /// public int McusPerColumn { get; set; } @@ -91,7 +91,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder } /// - /// Allocates the frame component blocks + /// Allocates the frame component blocks. /// public void InitComponents() { diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ProfileResolver.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ProfileResolver.cs index 3e7108b151..abfd6f556c 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ProfileResolver.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ProfileResolver.cs @@ -12,30 +12,30 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder internal static class ProfileResolver { /// - /// Describes the EXIF specific markers + /// Describes the EXIF specific markers. /// public static readonly byte[] JFifMarker = Encoding.ASCII.GetBytes("JFIF\0"); /// - /// Describes the EXIF specific markers + /// Describes the EXIF specific markers. /// public static readonly byte[] IccMarker = Encoding.ASCII.GetBytes("ICC_PROFILE\0"); /// - /// Describes the ICC specific markers + /// Describes the ICC specific markers. /// public static readonly byte[] ExifMarker = Encoding.ASCII.GetBytes("Exif\0\0"); /// - /// Describes Adobe specific markers + /// Describes Adobe specific markers . /// public static readonly byte[] AdobeMarker = Encoding.ASCII.GetBytes("Adobe"); /// - /// Returns a value indicating whether the passed bytes are a match to the profile identifier + /// Returns a value indicating whether the passed bytes are a match to the profile identifier. /// - /// The bytes to check - /// The profile identifier + /// The bytes to check. + /// The profile identifier. /// The public static bool IsProfile(ReadOnlySpan bytesToCheck, ReadOnlySpan profileIdentifier) { diff --git a/src/ImageSharp/IImageInfo.cs b/src/ImageSharp/IImageInfo.cs index 6e64aa679a..b1b1afc66f 100644 --- a/src/ImageSharp/IImageInfo.cs +++ b/src/ImageSharp/IImageInfo.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp { /// /// Encapsulates properties that describe basic image information including dimensions, pixel type information - /// and additional metadata + /// and additional metadata. /// public interface IImageInfo { diff --git a/src/ImageSharp/IO/DoubleBufferedStreamReader.cs b/src/ImageSharp/IO/DoubleBufferedStreamReader.cs index 8530ecec52..6d5673d5a4 100644 --- a/src/ImageSharp/IO/DoubleBufferedStreamReader.cs +++ b/src/ImageSharp/IO/DoubleBufferedStreamReader.cs @@ -13,10 +13,10 @@ namespace SixLabors.ImageSharp.IO /// A stream reader that add a secondary level buffer in addition to native stream buffered reading /// to reduce the overhead of small incremental reads. ///
- internal class DoubleBufferedStreamReader : IDisposable + internal sealed class DoubleBufferedStreamReader : IDisposable { /// - /// The length, in bytes, of the buffering chunk + /// The length, in bytes, of the buffering chunk. /// public const int ChunkLength = 4096; @@ -49,12 +49,12 @@ namespace SixLabors.ImageSharp.IO } /// - /// Gets the length, in bytes, of the stream + /// Gets the length, in bytes, of the stream. /// public long Length => this.length; /// - /// Gets or sets the current position within the stream + /// Gets or sets the current position within the stream. /// public long Position { diff --git a/src/ImageSharp/IO/Endianness.cs b/src/ImageSharp/IO/Endianness.cs deleted file mode 100644 index 59b2ae77c1..0000000000 --- a/src/ImageSharp/IO/Endianness.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -namespace SixLabors.ImageSharp.IO -{ - /// - /// Endianness of a converter - /// - internal enum Endianness - { - /// - /// Little endian - least significant byte first - /// - LittleEndian, - - /// - /// Big endian - most significant byte first - /// - BigEndian - } -} diff --git a/src/ImageSharp/IO/LocalFileSystem.cs b/src/ImageSharp/IO/LocalFileSystem.cs index dc5901ff92..11f3d79723 100644 --- a/src/ImageSharp/IO/LocalFileSystem.cs +++ b/src/ImageSharp/IO/LocalFileSystem.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.IO /// /// A wrapper around the local File apis. /// - internal class LocalFileSystem : IFileSystem + internal sealed class LocalFileSystem : IFileSystem { /// public Stream OpenRead(string path) => File.OpenRead(path); diff --git a/src/ImageSharp/Image.FromBytes.cs b/src/ImageSharp/Image.FromBytes.cs index 34927e6e2b..7ceeea9498 100644 --- a/src/ImageSharp/Image.FromBytes.cs +++ b/src/ImageSharp/Image.FromBytes.cs @@ -3,7 +3,6 @@ using System; using System.IO; -using System.Linq; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; @@ -32,7 +31,7 @@ namespace SixLabors.ImageSharp /// The mime type or null if none found. public static IImageFormat DetectFormat(Configuration config, byte[] data) { - using (Stream stream = new MemoryStream(data)) + using (var stream = new MemoryStream(data)) { return DetectFormat(config, stream); } @@ -118,9 +117,9 @@ namespace SixLabors.ImageSharp public static Image Load(Configuration config, byte[] data) where TPixel : struct, IPixel { - using (var memoryStream = new MemoryStream(data)) + using (var steram = new MemoryStream(data)) { - return Load(config, memoryStream); + return Load(config, steram); } } @@ -135,9 +134,9 @@ namespace SixLabors.ImageSharp public static Image Load(Configuration config, byte[] data, out IImageFormat format) where TPixel : struct, IPixel { - using (var memoryStream = new MemoryStream(data)) + using (var stream = new MemoryStream(data)) { - return Load(config, memoryStream, out format); + return Load(config, stream, out format); } } @@ -151,9 +150,9 @@ namespace SixLabors.ImageSharp public static Image Load(byte[] data, IImageDecoder decoder) where TPixel : struct, IPixel { - using (var memoryStream = new MemoryStream(data)) + using (var stream = new MemoryStream(data)) { - return Load(memoryStream, decoder); + return Load(stream, decoder); } } diff --git a/src/ImageSharp/Image.WrapMemory.cs b/src/ImageSharp/Image.WrapMemory.cs index c2935bed93..4bb24a126b 100644 --- a/src/ImageSharp/Image.WrapMemory.cs +++ b/src/ImageSharp/Image.WrapMemory.cs @@ -7,7 +7,6 @@ using System.Buffers; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.MetaData; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; namespace SixLabors.ImageSharp { @@ -22,10 +21,10 @@ namespace SixLabors.ImageSharp ///
/// The pixel type /// The - /// The pixel memory - /// The width of the memory image - /// The height of the memory image - /// The + /// The pixel memory. + /// The width of the memory image. + /// The height of the memory image. + /// The . /// An instance public static Image WrapMemory( Configuration config, @@ -45,10 +44,10 @@ namespace SixLabors.ImageSharp ///
/// The pixel type /// The - /// The pixel memory - /// The width of the memory image - /// The height of the memory image - /// An instance + /// The pixel memory. + /// The width of the memory image. + /// The height of the memory image. + /// An instance. public static Image WrapMemory( Configuration config, Memory pixelMemory, @@ -64,11 +63,11 @@ namespace SixLabors.ImageSharp /// allowing to view/manipulate it as an ImageSharp instance. /// The memory is being observed, the caller remains responsible for managing it's lifecycle. ///
- /// The pixel type - /// The pixel memory - /// The width of the memory image - /// The height of the memory image - /// An instance + /// The pixel type. + /// The pixel memory. + /// The width of the memory image. + /// The height of the memory image. + /// An instance. public static Image WrapMemory( Memory pixelMemory, int width, @@ -88,8 +87,8 @@ namespace SixLabors.ImageSharp /// The pixel type /// The /// The that is being transferred to the image - /// The width of the memory image - /// The height of the memory image + /// The width of the memory image. + /// The height of the memory image. /// The /// An instance public static Image WrapMemory( @@ -111,11 +110,11 @@ namespace SixLabors.ImageSharp /// meaning that the caller is not allowed to dispose . /// It will be disposed together with the result image. ///
- /// The pixel type + /// The pixel type. /// The - /// The that is being transferred to the image - /// The width of the memory image - /// The height of the memory image + /// The that is being transferred to the image. + /// The width of the memory image. + /// The height of the memory image. /// An instance public static Image WrapMemory( Configuration config, @@ -135,10 +134,10 @@ namespace SixLabors.ImageSharp /// It will be disposed together with the result image. ///
/// The pixel type - /// The that is being transferred to the image - /// The width of the memory image - /// The height of the memory image - /// An instance + /// The that is being transferred to the image. + /// The width of the memory image. + /// The height of the memory image. + /// An instance. public static Image WrapMemory( IMemoryOwner pixelMemoryOwner, int width, diff --git a/src/ImageSharp/ImageExtensions.Internal.cs b/src/ImageSharp/ImageExtensions.Internal.cs index dfdbbd89b8..5b5e566659 100644 --- a/src/ImageSharp/ImageExtensions.Internal.cs +++ b/src/ImageSharp/ImageExtensions.Internal.cs @@ -3,7 +3,6 @@ using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; namespace SixLabors.ImageSharp { diff --git a/src/ImageSharp/ImageExtensions.cs b/src/ImageSharp/ImageExtensions.cs index cbf93275c8..5010451b8e 100644 --- a/src/ImageSharp/ImageExtensions.cs +++ b/src/ImageSharp/ImageExtensions.cs @@ -12,7 +12,7 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp { /// - /// Extension methods over Image{TPixel} + /// Extension methods over Image{TPixel}. /// public static partial class ImageExtensions { @@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp if (format is null) { var sb = new StringBuilder(); - sb.AppendLine($"Can't find a format that is associated with the file extension '{ext}'. Registered formats with there extensions include:"); + sb.AppendLine($"No encoder was found for extension '{ext}'. Registered encoders include:"); foreach (IImageFormat fmt in source.GetConfiguration().ImageFormats) { sb.AppendLine($" - {fmt.Name} : {string.Join(", ", fmt.FileExtensions)}"); @@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp if (encoder is null) { var sb = new StringBuilder(); - sb.AppendLine($"Can't find encoder for file extension '{ext}' using image format '{format.Name}'. Registered encoders include:"); + sb.AppendLine($"No encoder was found for extension '{ext}' using image format '{format.Name}'. Registered encoders include:"); foreach (KeyValuePair enc in source.GetConfiguration().ImageFormatsManager.ImageEncoders) { sb.AppendLine($" - {enc.Key} : {enc.Value.GetType().Name}"); @@ -94,7 +94,7 @@ namespace SixLabors.ImageSharp if (encoder is null) { var sb = new StringBuilder(); - sb.AppendLine("Can't find encoder for provided mime type. Available encoded:"); + sb.AppendLine("No encoder was found for the provided mime type. Registered encoders include:"); foreach (KeyValuePair val in source.GetConfiguration().ImageFormatsManager.ImageEncoders) { diff --git a/src/ImageSharp/Memory/RowInterval.cs b/src/ImageSharp/Memory/RowInterval.cs index 501ed5e69c..835e880e94 100644 --- a/src/ImageSharp/Memory/RowInterval.cs +++ b/src/ImageSharp/Memory/RowInterval.cs @@ -22,24 +22,21 @@ namespace SixLabors.ImageSharp.Memory } /// - /// Gets the INCLUSIVE minimum + /// Gets the INCLUSIVE minimum. /// public int Min { get; } /// - /// Gets the EXCLUSIVE maximum + /// Gets the EXCLUSIVE maximum. /// public int Max { get; } /// - /// Gets the difference ( - ) + /// Gets the difference ( - ). /// public int Height => this.Max - this.Min; /// - public override string ToString() - { - return $"RowInterval [{this.Min}->{this.Max}["; - } + public override string ToString() => $"RowInterval [{this.Min}->{this.Max}]"; } } \ No newline at end of file diff --git a/src/ImageSharp/MetaData/ImageProperty.cs b/src/ImageSharp/MetaData/ImageProperty.cs index 24a3686de2..6d38b6bf13 100644 --- a/src/ImageSharp/MetaData/ImageProperty.cs +++ b/src/ImageSharp/MetaData/ImageProperty.cs @@ -107,10 +107,7 @@ namespace SixLabors.ImageSharp.MetaData /// /// A containing a fully qualified type name. /// - public override string ToString() - { - return $"ImageProperty [ Name={this.Name}, Value={this.Value} ]"; - } + public override string ToString() => $"ImageProperty [ Name={this.Name}, Value={this.Value} ]"; /// /// Indicates whether the current object is equal to another object of the same type. diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs index 8ec9eea275..cf95affb04 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs @@ -8,20 +8,19 @@ using System.Collections.ObjectModel; using System.Linq; using System.Runtime.CompilerServices; using System.Text; -using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.MetaData.Profiles.Exif { /// - /// Reads and parses EXIF data from a byte array + /// Reads and parses EXIF data from a byte array. /// internal sealed class ExifReader { private List invalidTags; private readonly byte[] exifData; private int position; - private Endianness endianness = Endianness.BigEndian; + private bool isBigEndian; private uint exifOffset; private uint gpsOffset; @@ -38,12 +37,12 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif public IReadOnlyList InvalidTags => this.invalidTags ?? (IReadOnlyList)Array.Empty(); /// - /// Gets the thumbnail length in the byte stream + /// Gets the thumbnail length in the byte stream. /// public uint ThumbnailLength { get; private set; } /// - /// Gets the thumbnail offset position in the byte stream + /// Gets the thumbnail offset position in the byte stream. /// public uint ThumbnailOffset { get; private set; } @@ -74,10 +73,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif var values = new List(); // II == 0x4949 - if (this.ReadUInt16() == 0x4949) - { - this.endianness = Endianness.LittleEndian; - } + this.isBigEndian = !(this.ReadUInt16() == 0x4949); if (this.ReadUInt16() != 0x002A) { @@ -382,7 +378,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif [MethodImpl(InliningOptions.ShortMethod)] private TEnum ToEnum(int value, TEnum defaultValue) - where TEnum : struct + where TEnum : struct, Enum { if (EnumHelper.IsDefined(value)) { @@ -458,7 +454,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif return default; } - long intValue = this.endianness == Endianness.BigEndian + long intValue = this.isBigEndian ? BinaryPrimitives.ReadInt64BigEndian(buffer) : BinaryPrimitives.ReadInt64LittleEndian(buffer); @@ -473,7 +469,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif return default; } - return this.endianness == Endianness.BigEndian + return this.isBigEndian ? BinaryPrimitives.ReadUInt32BigEndian(buffer) : BinaryPrimitives.ReadUInt32LittleEndian(buffer); } @@ -485,7 +481,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif return default; } - return this.endianness == Endianness.BigEndian + return this.isBigEndian ? BinaryPrimitives.ReadUInt16BigEndian(buffer) : BinaryPrimitives.ReadUInt16LittleEndian(buffer); } @@ -497,7 +493,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif return default; } - int intValue = this.endianness == Endianness.BigEndian + int intValue = this.isBigEndian ? BinaryPrimitives.ReadInt32BigEndian(buffer) : BinaryPrimitives.ReadInt32LittleEndian(buffer); @@ -526,7 +522,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif return default; } - return this.endianness == Endianness.BigEndian + return this.isBigEndian ? BinaryPrimitives.ReadInt32BigEndian(buffer) : BinaryPrimitives.ReadInt32LittleEndian(buffer); } @@ -551,13 +547,13 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif return default; } - return this.endianness == Endianness.BigEndian + return this.isBigEndian ? BinaryPrimitives.ReadInt16BigEndian(buffer) : BinaryPrimitives.ReadInt16LittleEndian(buffer); } - private class EnumHelper - where TEnum : struct + private sealed class EnumHelper + where TEnum : struct, Enum { private static readonly int[] Values = Enum.GetValues(typeof(TEnum)).Cast() .Select(e => Convert.ToInt32(e)).OrderBy(e => e).ToArray(); diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccColorantTableEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccColorantTableEntry.cs index 8f273dd603..4bcd1b6861 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccColorantTableEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccColorantTableEntry.cs @@ -35,22 +35,22 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc } /// - /// Gets the colorant name + /// Gets the colorant name. /// public string Name { get; } /// - /// Gets the first PCS value + /// Gets the first PCS value. /// public ushort Pcs1 { get; } /// - /// Gets the second PCS value + /// Gets the second PCS value. /// public ushort Pcs2 { get; } /// - /// Gets the third PCS value + /// Gets the third PCS value. /// public ushort Pcs3 { get; } @@ -110,9 +110,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc } /// - public override string ToString() - { - return $"{this.Name}: {this.Pcs1}; {this.Pcs2}; {this.Pcs3}"; - } + public override string ToString() => $"{this.Name}: {this.Pcs1}; {this.Pcs2}; {this.Pcs3}"; } } diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccLocalizedString.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccLocalizedString.cs index 00ededca4d..1920e97fe1 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccLocalizedString.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccLocalizedString.cs @@ -49,9 +49,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc this.Text == other.Text; /// - public override string ToString() - { - return $"{this.Culture.Name}: {this.Text}"; - } + public override string ToString() => $"{this.Culture.Name}: {this.Text}"; } } diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileId.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileId.cs index f64d5409ae..710e6bb252 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileId.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileId.cs @@ -31,27 +31,27 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc } /// - /// Gets the first part of the ID + /// Gets the first part of the ID. /// public uint Part1 { get; } /// - /// Gets the second part of the ID + /// Gets the second part of the ID. /// public uint Part2 { get; } /// - /// Gets the third part of the ID + /// Gets the third part of the ID. /// public uint Part3 { get; } /// - /// Gets the fourth part of the ID + /// Gets the fourth part of the ID. /// public uint Part4 { get; } /// - /// Gets a value indicating whether the ID is set or just consists of zeros + /// Gets a value indicating whether the ID is set or just consists of zeros. /// public bool IsSet => !this.Equals(Zero); @@ -86,10 +86,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc } /// - public override bool Equals(object obj) - { - return obj is IccProfileId other && this.Equals(other); - } + public override bool Equals(object obj) => obj is IccProfileId other && this.Equals(other); /// public bool Equals(IccProfileId other) => @@ -109,14 +106,8 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc } /// - public override string ToString() - { - return $"{ToHex(this.Part1)}-{ToHex(this.Part2)}-{ToHex(this.Part3)}-{ToHex(this.Part4)}"; - } + public override string ToString() => $"{ToHex(this.Part1)}-{ToHex(this.Part2)}-{ToHex(this.Part3)}-{ToHex(this.Part4)}"; - private static string ToHex(uint value) - { - return value.ToString("X").PadLeft(8, '0'); - } + private static string ToHex(uint value) => value.ToString("X").PadLeft(8, '0'); } } \ No newline at end of file diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccScreeningChannel.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccScreeningChannel.cs index e8885a66bf..7e2072d5bb 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccScreeningChannel.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccScreeningChannel.cs @@ -26,12 +26,12 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc } /// - /// Gets the screen frequency + /// Gets the screen frequency. /// public float Frequency { get; } /// - /// Gets the angle in degrees + /// Gets the angle in degrees. /// public float Angle { get; } @@ -89,9 +89,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc } /// - public override string ToString() - { - return $"{this.Frequency}Hz; {this.Angle}°; {this.SpotShape}"; - } + public override string ToString() => $"{this.Frequency}Hz; {this.Angle}°; {this.SpotShape}"; } } \ No newline at end of file diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccTagTableEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccTagTableEntry.cs index d93e068c52..7713c4faa1 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccTagTableEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccTagTableEntry.cs @@ -24,17 +24,17 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc } /// - /// Gets the signature of the tag + /// Gets the signature of the tag. /// public IccProfileTag Signature { get; } /// - /// Gets the offset of entry in bytes + /// Gets the offset of entry in bytes. /// public uint Offset { get; } /// - /// Gets the size of entry in bytes + /// Gets the size of entry in bytes. /// public uint DataSize { get; } @@ -69,10 +69,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc } /// - public override bool Equals(object obj) - { - return obj is IccTagTableEntry other && this.Equals(other); - } + public override bool Equals(object obj) => obj is IccTagTableEntry other && this.Equals(other); /// public bool Equals(IccTagTableEntry other) => @@ -81,15 +78,9 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc this.DataSize.Equals(other.DataSize); /// - public override int GetHashCode() - { - return HashCode.Combine(this.Signature, this.Offset, this.DataSize); - } + public override int GetHashCode() => HashCode.Combine(this.Signature, this.Offset, this.DataSize); /// - public override string ToString() - { - return $"{this.Signature} (Offset: {this.Offset}; Size: {this.DataSize})"; - } + public override string ToString() => $"{this.Signature} (Offset: {this.Offset}; Size: {this.DataSize})"; } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs index 82698d5085..8a9368463f 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs @@ -164,10 +164,10 @@ namespace SixLabors.ImageSharp.PixelFormats { vector = Vector4.Clamp(vector, MinusOne, Vector4.One) * Half; - uint byte4 = ((uint)Math.Round(vector.X) & 0xFF) << 0; - uint byte3 = ((uint)Math.Round(vector.Y) & 0xFF) << 8; - uint byte2 = ((uint)Math.Round(vector.Z) & 0xFF) << 16; - uint byte1 = ((uint)Math.Round(vector.W) & 0xFF) << 24; + uint byte4 = ((uint)MathF.Round(vector.X) & 0xFF) << 0; + uint byte3 = ((uint)MathF.Round(vector.Y) & 0xFF) << 8; + uint byte2 = ((uint)MathF.Round(vector.Z) & 0xFF) << 16; + uint byte1 = ((uint)MathF.Round(vector.W) & 0xFF) << 24; return byte4 | byte3 | byte2 | byte1; } diff --git a/src/ImageSharp/Primitives/LongRational.cs b/src/ImageSharp/Primitives/LongRational.cs index d790b110d0..b15aa4022f 100644 --- a/src/ImageSharp/Primitives/LongRational.cs +++ b/src/ImageSharp/Primitives/LongRational.cs @@ -8,7 +8,7 @@ using System.Text; namespace SixLabors.ImageSharp.Primitives { /// - /// Represents a number that can be expressed as a fraction + /// Represents a number that can be expressed as a fraction. /// /// /// This is a very simplified implementation of a rational number designed for use with metadata only. diff --git a/src/ImageSharp/Primitives/ValueSize.cs b/src/ImageSharp/Primitives/ValueSize.cs index e5e086540d..44bee50309 100644 --- a/src/ImageSharp/Primitives/ValueSize.cs +++ b/src/ImageSharp/Primitives/ValueSize.cs @@ -28,22 +28,22 @@ namespace SixLabors.ImageSharp.Primitives } /// - /// Enumerates the different value types + /// Enumerates the different value types. /// public enum ValueSizeType { /// - /// The value is the final return value + /// The value is the final return value. /// Absolute, /// - /// The value is a percentage of the image width + /// The value is a percentage of the image width. /// PercentageOfWidth, /// - /// The value is a percentage of the images height + /// The value is a percentage of the images height. /// PercentageOfHeight } @@ -59,11 +59,10 @@ namespace SixLabors.ImageSharp.Primitives public ValueSizeType Type { get; } /// - /// Implicitly converts a float into an absolute value + /// Implicitly converts a float into an absolute value. /// /// the value to use as the absolute figure. - public static implicit operator ValueSize(float f) - => Absolute(f); + public static implicit operator ValueSize(float f) => Absolute(f); /// /// Create a new ValueSize with as a PercentageOfWidth type with value set to percentage. @@ -89,7 +88,7 @@ namespace SixLabors.ImageSharp.Primitives /// Create a new ValueSize with as a Absolute type with value set to value. /// /// The value. - /// a Values size with type Absolute( + /// a Values size with type Absolute. public static ValueSize Absolute(float value) { return new ValueSize(value, ValueSizeType.Absolute); @@ -99,7 +98,7 @@ namespace SixLabors.ImageSharp.Primitives /// Calculates the specified size. /// /// The size. - /// The calculated value + /// The calculated value. public float Calculate(Size size) { switch (this.Type) @@ -115,10 +114,7 @@ namespace SixLabors.ImageSharp.Primitives } /// - public override string ToString() - { - return $"{this.Value} - {this.Type}"; - } + public override string ToString() => $"{this.Value} - {this.Type}"; /// public override bool Equals(object obj) From 51a6647e5bc26db52d7bd580fe1639a7bd23b1bb Mon Sep 17 00:00:00 2001 From: Poyo Date: Wed, 3 Apr 2019 05:32:32 -0700 Subject: [PATCH 046/223] Fix incorrect gradient color-stop calculation (#864) * Fix incorrect gradient color-stop calculation * Update submodule * Add multi-stop gradient test * Add missing reference image --- .../Processing/GradientBrushBase{TPixel}.cs | 2 +- .../Drawing/FillLinearGradientBrushTests.cs | 47 +++++++++++++++++++ tests/Images/External | 2 +- 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp.Drawing/Processing/GradientBrushBase{TPixel}.cs b/src/ImageSharp.Drawing/Processing/GradientBrushBase{TPixel}.cs index 00141a8d8d..ca565c46c9 100644 --- a/src/ImageSharp.Drawing/Processing/GradientBrushBase{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/GradientBrushBase{TPixel}.cs @@ -121,7 +121,7 @@ namespace SixLabors.ImageSharp.Processing { var fromAsVector = from.Color.ToVector4(); var toAsVector = to.Color.ToVector4(); - float onLocalGradient = (positionOnCompleteGradient - from.Ratio) / to.Ratio; + float onLocalGradient = (positionOnCompleteGradient - from.Ratio) / (to.Ratio - from.Ratio); // TODO: this should be changeble for different gradienting functions Vector4 result = PorterDuffFunctions.NormalSrcOver( diff --git a/tests/ImageSharp.Tests/Drawing/FillLinearGradientBrushTests.cs b/tests/ImageSharp.Tests/Drawing/FillLinearGradientBrushTests.cs index 556ec9c9ca..0bab2c079b 100644 --- a/tests/ImageSharp.Tests/Drawing/FillLinearGradientBrushTests.cs +++ b/tests/ImageSharp.Tests/Drawing/FillLinearGradientBrushTests.cs @@ -350,5 +350,52 @@ namespace SixLabors.ImageSharp.Tests.Drawing false, false); } + + [Theory] + [WithBlankImages(200, 200, PixelTypes.Rgba32, 0, 0, 199, 199, new[] { 0f, .25f, .5f, .75f, 1f }, new[] { 0, 1, 2, 3, 4 })] + public void MultiplePointGradients( + TestImageProvider provider, + int startX, int startY, + int endX, int endY, + float[] stopPositions, + int[] stopColorCodes) + where TPixel : struct, IPixel + { + TPixel[] colors = + { + NamedColors.Black, NamedColors.Blue, NamedColors.Red, + NamedColors.White, NamedColors.Lime + }; + + var coloringVariant = new StringBuilder(); + var colorStops = new ColorStop[stopPositions.Length]; + + for (int i = 0; i < stopPositions.Length; i++) + { + TPixel color = colors[stopColorCodes[i % colors.Length]]; + float position = stopPositions[i]; + colorStops[i] = new ColorStop(position, color); + Rgba32 rgba = default; + color.ToRgba32(ref rgba); + coloringVariant.AppendFormat(CultureInfo.InvariantCulture, "{0}@{1};", rgba.ToHex(), position); + } + + FormattableString variant = $"({startX},{startY})_TO_({endX},{endY})__[{coloringVariant}]"; + + provider.VerifyOperation( + image => + { + var unicolorLinearGradientBrush = new LinearGradientBrush( + new SixLabors.Primitives.Point(startX, startY), + new SixLabors.Primitives.Point(endX, endY), + GradientRepetitionMode.None, + colorStops); + + image.Mutate(x => x.Fill(unicolorLinearGradientBrush)); + }, + variant, + false, + false); + } } } \ No newline at end of file diff --git a/tests/Images/External b/tests/Images/External index 9d71985545..bc2765fb94 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 9d71985545bb827270ca34af3f78e4c220560ef1 +Subproject commit bc2765fb949d5d4ddba289b29174022bb94646de From d382faf0f783ee8237d9c71e662fda7d5fad9689 Mon Sep 17 00:00:00 2001 From: Poyo Date: Wed, 3 Apr 2019 15:12:31 -0700 Subject: [PATCH 047/223] Change existing gradient brushes to accept PointF (#865) * Change existing gradient brushes to accept PointF * Change PositionOnGradient to accept float * Remove invalid assert. --- .../EllipticGradientBrush{TPixel}.cs | 18 +++++++-------- .../Processing/GradientBrushBase{TPixel}.cs | 12 +++++----- .../Processing/LinearGradientBrush{TPixel}.cs | 22 +++++++++---------- .../Processing/RadialGradientBrush{TPixel}.cs | 10 ++++----- .../Drawing/FillLinearGradientBrushTests.cs | 4 ---- tests/Images/External | 2 +- 6 files changed, 32 insertions(+), 36 deletions(-) diff --git a/src/ImageSharp.Drawing/Processing/EllipticGradientBrush{TPixel}.cs b/src/ImageSharp.Drawing/Processing/EllipticGradientBrush{TPixel}.cs index 8af01564c4..64901c8e80 100644 --- a/src/ImageSharp.Drawing/Processing/EllipticGradientBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/EllipticGradientBrush{TPixel}.cs @@ -18,9 +18,9 @@ namespace SixLabors.ImageSharp.Processing public sealed class EllipticGradientBrush : GradientBrushBase where TPixel : struct, IPixel { - private readonly Point center; + private readonly PointF center; - private readonly Point referenceAxisEnd; + private readonly PointF referenceAxisEnd; private readonly float axisRatio; @@ -35,8 +35,8 @@ namespace SixLabors.ImageSharp.Processing /// Defines how the colors of the gradients are repeated. /// the color stops as defined in base class. public EllipticGradientBrush( - Point center, - Point referenceAxisEnd, + PointF center, + PointF referenceAxisEnd, float axisRatio, GradientRepetitionMode repetitionMode, params ColorStop[] colorStops) @@ -64,9 +64,9 @@ namespace SixLabors.ImageSharp.Processing /// private sealed class RadialGradientBrushApplicator : GradientBrushApplicatorBase { - private readonly Point center; + private readonly PointF center; - private readonly Point referenceAxisEnd; + private readonly PointF referenceAxisEnd; private readonly float axisRatio; @@ -99,8 +99,8 @@ namespace SixLabors.ImageSharp.Processing public RadialGradientBrushApplicator( ImageFrame target, GraphicsOptions options, - Point center, - Point referenceAxisEnd, + PointF center, + PointF referenceAxisEnd, float axisRatio, ColorStop[] colorStops, GradientRepetitionMode repetitionMode) @@ -129,7 +129,7 @@ namespace SixLabors.ImageSharp.Processing } /// - protected override float PositionOnGradient(int xt, int yt) + protected override float PositionOnGradient(float xt, float yt) { float x0 = xt - this.center.X; float y0 = yt - this.center.Y; diff --git a/src/ImageSharp.Drawing/Processing/GradientBrushBase{TPixel}.cs b/src/ImageSharp.Drawing/Processing/GradientBrushBase{TPixel}.cs index ca565c46c9..2dfae9e8f1 100644 --- a/src/ImageSharp.Drawing/Processing/GradientBrushBase{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/GradientBrushBase{TPixel}.cs @@ -81,7 +81,7 @@ namespace SixLabors.ImageSharp.Processing { get { - float positionOnCompleteGradient = this.PositionOnGradient(x, y); + float positionOnCompleteGradient = this.PositionOnGradient(x + 0.5f, y + 0.5f); switch (this.repetitionMode) { @@ -137,18 +137,18 @@ namespace SixLabors.ImageSharp.Processing } /// - /// calculates the position on the gradient for a given pixel. + /// calculates the position on the gradient for a given point. /// This method is abstract as it's content depends on the shape of the gradient. /// - /// The x coordinate of the pixel - /// The y coordinate of the pixel + /// The x coordinate of the point + /// The y coordinate of the point /// - /// The position the given pixel has on the gradient. + /// The position the given point has on the gradient. /// The position is not bound to the [0..1] interval. /// Values outside of that interval may be treated differently, /// e.g. for the enum. /// - protected abstract float PositionOnGradient(int x, int y); + protected abstract float PositionOnGradient(float x, float y); private (ColorStop from, ColorStop to) GetGradientSegment( float positionOnCompleteGradient) diff --git a/src/ImageSharp.Drawing/Processing/LinearGradientBrush{TPixel}.cs b/src/ImageSharp.Drawing/Processing/LinearGradientBrush{TPixel}.cs index 765bf5499d..efdc852f28 100644 --- a/src/ImageSharp.Drawing/Processing/LinearGradientBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/LinearGradientBrush{TPixel}.cs @@ -17,9 +17,9 @@ namespace SixLabors.ImageSharp.Processing public sealed class LinearGradientBrush : GradientBrushBase where TPixel : struct, IPixel { - private readonly Point p1; + private readonly PointF p1; - private readonly Point p2; + private readonly PointF p2; /// /// Initializes a new instance of the class. @@ -29,8 +29,8 @@ namespace SixLabors.ImageSharp.Processing /// defines how colors are repeated. /// public LinearGradientBrush( - Point p1, - Point p2, + PointF p1, + PointF p2, GradientRepetitionMode repetitionMode, params ColorStop[] colorStops) : base(repetitionMode, colorStops) @@ -48,9 +48,9 @@ namespace SixLabors.ImageSharp.Processing /// private sealed class LinearGradientBrushApplicator : GradientBrushApplicatorBase { - private readonly Point start; + private readonly PointF start; - private readonly Point end; + private readonly PointF end; /// /// the vector along the gradient, x component @@ -93,8 +93,8 @@ namespace SixLabors.ImageSharp.Processing /// the graphics options public LinearGradientBrushApplicator( ImageFrame source, - Point start, - Point end, + PointF start, + PointF end, ColorStop[] colorStops, GradientRepetitionMode repetitionMode, GraphicsOptions options) @@ -116,15 +116,15 @@ namespace SixLabors.ImageSharp.Processing this.length = (float)Math.Sqrt(this.alongsSquared); } - protected override float PositionOnGradient(int x, int y) + protected override float PositionOnGradient(float x, float y) { if (this.acrossX == 0) { - return (x - this.start.X) / (float)(this.end.X - this.start.X); + return (x - this.start.X) / (this.end.X - this.start.X); } else if (this.acrossY == 0) { - return (y - this.start.Y) / (float)(this.end.Y - this.start.Y); + return (y - this.start.Y) / (this.end.Y - this.start.Y); } else { diff --git a/src/ImageSharp.Drawing/Processing/RadialGradientBrush{TPixel}.cs b/src/ImageSharp.Drawing/Processing/RadialGradientBrush{TPixel}.cs index 16380fc34b..d33d099931 100644 --- a/src/ImageSharp.Drawing/Processing/RadialGradientBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/RadialGradientBrush{TPixel}.cs @@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Processing public sealed class RadialGradientBrush : GradientBrushBase where TPixel : struct, IPixel { - private readonly Point center; + private readonly PointF center; private readonly float radius; @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Processing /// Defines how the colors in the gradient are repeated. /// the color stops as defined in base class. public RadialGradientBrush( - Point center, + PointF center, float radius, GradientRepetitionMode repetitionMode, params ColorStop[] colorStops) @@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp.Processing /// private sealed class RadialGradientBrushApplicator : GradientBrushApplicatorBase { - private readonly Point center; + private readonly PointF center; private readonly float radius; @@ -67,7 +67,7 @@ namespace SixLabors.ImageSharp.Processing public RadialGradientBrushApplicator( ImageFrame target, GraphicsOptions options, - Point center, + PointF center, float radius, ColorStop[] colorStops, GradientRepetitionMode repetitionMode) @@ -89,7 +89,7 @@ namespace SixLabors.ImageSharp.Processing /// The X coordinate of the target pixel. /// The Y coordinate of the target pixel. /// the position on the color gradient. - protected override float PositionOnGradient(int x, int y) + protected override float PositionOnGradient(float x, float y) { float distance = (float)Math.Sqrt(Math.Pow(this.center.X - x, 2) + Math.Pow(this.center.Y - y, 2)); return distance / this.radius; diff --git a/tests/ImageSharp.Tests/Drawing/FillLinearGradientBrushTests.cs b/tests/ImageSharp.Tests/Drawing/FillLinearGradientBrushTests.cs index 0bab2c079b..0f4a98a24a 100644 --- a/tests/ImageSharp.Tests/Drawing/FillLinearGradientBrushTests.cs +++ b/tests/ImageSharp.Tests/Drawing/FillLinearGradientBrushTests.cs @@ -275,10 +275,6 @@ namespace SixLabors.ImageSharp.Tests.Drawing int verticalSign = startY == 0 ? 1 : -1; int horizontalSign = startX == 0 ? 1 : -1; - // check first and last pixel, these are known: - Assert.Equal(red, image[startX, startY]); - Assert.Equal(yellow, image[endX, endY]); - for (int i = 0; i < image.Height; i++) { // it's diagonal, so for any (a, a) on the gradient line, for all (a-x, b+x) - +/- depending on the diagonal direction - must be the same color) diff --git a/tests/Images/External b/tests/Images/External index bc2765fb94..94b5a8e11b 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit bc2765fb949d5d4ddba289b29174022bb94646de +Subproject commit 94b5a8e11b33ba62c15d1d03f1b8b721468764f1 From 3c0d9bb487d9ee58eac2f79858d10effed0601cb Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 7 Apr 2019 20:06:27 +0200 Subject: [PATCH 048/223] PixelConversionModifierExtensions.ApplyCompanding() + tests --- .../PixelConversionModifiersExtensions.cs | 18 +++++ .../Transforms/Resize/ResizeProcessor.cs | 7 +- ...PixelConversionModifiersExtensionsTests.cs | 65 +++++++++++++++++++ 3 files changed, 85 insertions(+), 5 deletions(-) create mode 100644 tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelConversionModifiersExtensionsTests.cs diff --git a/src/ImageSharp/PixelFormats/PixelConversionModifiersExtensions.cs b/src/ImageSharp/PixelFormats/PixelConversionModifiersExtensions.cs index bf77f85114..529041481f 100644 --- a/src/ImageSharp/PixelFormats/PixelConversionModifiersExtensions.cs +++ b/src/ImageSharp/PixelFormats/PixelConversionModifiersExtensions.cs @@ -5,6 +5,9 @@ using System.Runtime.CompilerServices; namespace SixLabors.ImageSharp.PixelFormats { + /// + /// Extension and utility methods for . + /// internal static class PixelConversionModifiersExtensions { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -16,5 +19,20 @@ namespace SixLabors.ImageSharp.PixelFormats this PixelConversionModifiers modifiers, PixelConversionModifiers removeThis) => modifiers & ~removeThis; + + /// + /// Applies the union of and , + /// if is true, returns unmodified otherwise. + /// + /// + /// and + /// should be always used together! + /// + public static PixelConversionModifiers ApplyCompanding( + this PixelConversionModifiers originalModifiers, + bool compand) => + compand + ? originalModifiers | PixelConversionModifiers.Scale | PixelConversionModifiers.SRgbCompand + : originalModifiers; } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs index 2c186a276b..198427d284 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs @@ -236,11 +236,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms int sourceHeight = source.Height; - PixelConversionModifiers conversionModifiers = PixelConversionModifiers.Premultiply; - if (this.Compand) - { - conversionModifiers |= PixelConversionModifiers.Scale | PixelConversionModifiers.SRgbCompand; - } + PixelConversionModifiers conversionModifiers = + PixelConversionModifiers.Premultiply.ApplyCompanding(this.Compand); // Interpolate the image using the calculated weights. // A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelConversionModifiersExtensionsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelConversionModifiersExtensionsTests.cs new file mode 100644 index 0000000000..e98e14fc62 --- /dev/null +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelConversionModifiersExtensionsTests.cs @@ -0,0 +1,65 @@ +// // Copyright (c) Six Labors and contributors. +// // Licensed under the Apache License, Version 2.0. +// // Copyright (c) Six Labors and contributors. +// // Licensed under the Apache License, Version 2.0. +// // Copyright (c) Six Labors and contributors. +// // Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations +{ + public class PixelConversionModifiersExtensionsTests + { + [Theory] + [InlineData(PixelConversionModifiers.None, PixelConversionModifiers.None, true)] + [InlineData(PixelConversionModifiers.None, PixelConversionModifiers.Premultiply, false)] + [InlineData(PixelConversionModifiers.SRgbCompand, PixelConversionModifiers.Premultiply, false)] + [InlineData( + PixelConversionModifiers.Premultiply | PixelConversionModifiers.Scale, + PixelConversionModifiers.Premultiply, + true)] + [InlineData( + PixelConversionModifiers.Premultiply | PixelConversionModifiers.Scale, + PixelConversionModifiers.Premultiply | PixelConversionModifiers.Scale, + true)] + [InlineData( + PixelConversionModifiers.Premultiply | PixelConversionModifiers.Scale, + PixelConversionModifiers.Scale, + true)] + internal void IsDefined( + PixelConversionModifiers baselineModifiers, + PixelConversionModifiers checkModifiers, + bool expected) + { + Assert.Equal(expected, baselineModifiers.IsDefined(checkModifiers)); + } + + [Theory] + [InlineData(PixelConversionModifiers.Premultiply | PixelConversionModifiers.Scale | PixelConversionModifiers.SRgbCompand, + PixelConversionModifiers.Scale, PixelConversionModifiers.Premultiply | PixelConversionModifiers.SRgbCompand)] + [InlineData(PixelConversionModifiers.None, PixelConversionModifiers.Premultiply, PixelConversionModifiers.None)] + internal void Remove( + PixelConversionModifiers baselineModifiers, + PixelConversionModifiers toRemove, + PixelConversionModifiers expected) + { + PixelConversionModifiers result = baselineModifiers.Remove(toRemove); + Assert.Equal(expected, result); + } + + [Theory] + [InlineData(PixelConversionModifiers.Premultiply, false, PixelConversionModifiers.Premultiply)] + [InlineData(PixelConversionModifiers.Premultiply, true, PixelConversionModifiers.Premultiply | PixelConversionModifiers.SRgbCompand | PixelConversionModifiers.Scale)] + internal void ApplyCompanding( + PixelConversionModifiers baselineModifiers, + bool compand, + PixelConversionModifiers expected) + { + PixelConversionModifiers result = baselineModifiers.ApplyCompanding(compand); + Assert.Equal(expected, result); + } + } +} \ No newline at end of file From a1479ee09b57e6d391ae7cc88dfb91605c1c4d57 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 7 Apr 2019 20:29:50 +0200 Subject: [PATCH 049/223] drop FileTestBase usage in ResizeTests --- .../Processing/Processors/Transforms/ResizeTests.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs index 034b66ae9a..34c9842481 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs @@ -15,7 +15,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms { - public class ResizeTests : FileTestBase + public class ResizeTests { public static readonly string[] CommonTestImages = { TestImages.Png.CalliphoraPartial }; @@ -31,6 +31,11 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms nameof(KnownResamplers.Lanczos5), }; + private const PixelTypes DefaultPixelType = PixelTypes.Rgba32; + + private const PixelTypes CommonNonDefaultPixelTypes = + PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.RgbaVector; + [Theory] [WithFileCollection(nameof(CommonTestImages), nameof(AllResamplerNames), DefaultPixelType, 0.5f, null, null)] [WithFileCollection(nameof(CommonTestImages), nameof(SmokeTestResamplerNames), DefaultPixelType, 0.3f, null, null)] @@ -185,7 +190,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms image.Mutate(x => x.Resize(image.Width / 2, image.Height / 2, KnownResamplers.Bicubic)); // Comparer fights decoder with gif-s. Could not use CompareToReferenceOutput here :( - image.DebugSave(provider, extension: Extensions.Gif); + image.DebugSave(provider, extension: "gif"); } } From 03879641929ed1e46e9251d759d72f2d2ce48695 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 7 Apr 2019 23:18:13 +0200 Subject: [PATCH 050/223] introduce [WithBasicTestPatternImages] --- .../Processors/Transforms/ResizeTests.cs | 17 +++++ .../WithBasicTestPatternImagesAttribute.cs | 37 +++++++++++ .../BasicTestPatternProvider.cs | 65 +++++++++++++++++++ .../ImageProviders/BlankProvider.cs | 3 + .../ImageProviders/SolidProvider.cs | 3 + .../ImageProviders/TestImageProvider.cs | 6 ++ .../ImageProviders/TestPatternProvider.cs | 5 +- .../Tests/TestImageProviderTests.cs | 14 ++++ 8 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 tests/ImageSharp.Tests/TestUtilities/Attributes/WithBasicTestPatternImagesAttribute.cs create mode 100644 tests/ImageSharp.Tests/TestUtilities/ImageProviders/BasicTestPatternProvider.cs diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs index 34c9842481..7f1f5a26a2 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs @@ -35,6 +35,23 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms private const PixelTypes CommonNonDefaultPixelTypes = PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.RgbaVector; + + [Theory] + [WithBasicTestPatternImages(15, 12, PixelTypes.Rgba32)] + public void Resize_BasicSmall(TestImageProvider provider) + where TPixel : struct, IPixel + { + // Basic test case, very helpful for debugging + // resizing: (15, 12) -> (10, 6) + // kernel dimensions: (3, 4) + + using (Image image = provider.GetImage()) + { + var destSize = new Size(image.Width * 2 / 3, image.Height / 2); + image.Mutate(x => x.Resize(destSize, KnownResamplers.Bicubic, false)); + image.DebugSave(provider); + } + } [Theory] [WithFileCollection(nameof(CommonTestImages), nameof(AllResamplerNames), DefaultPixelType, 0.5f, null, null)] diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithBasicTestPatternImagesAttribute.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithBasicTestPatternImagesAttribute.cs new file mode 100644 index 0000000000..1e4324e046 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithBasicTestPatternImagesAttribute.cs @@ -0,0 +1,37 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Reflection; + +namespace SixLabors.ImageSharp.Tests +{ + public class WithBasicTestPatternImagesAttribute : ImageDataAttributeBase + { + public WithBasicTestPatternImagesAttribute(int width, int height, PixelTypes pixelTypes, params object[] additionalParameters) + : this(null, width, height, pixelTypes, additionalParameters) + { + } + + public WithBasicTestPatternImagesAttribute(string memberData, int width, int height, PixelTypes pixelTypes, params object[] additionalParameters) + : base(memberData, pixelTypes, additionalParameters) + { + this.Width = width; + this.Height = height; + } + + /// + /// Gets the width + /// + public int Width { get; } + + /// + /// Gets the height + /// + public int Height { get; } + + protected override string GetFactoryMethodName(MethodInfo testMethod) => "BasicTestPattern"; + + protected override object[] GetFactoryMethodArgs(MethodInfo testMethod, Type factoryType) => new object[] { this.Width, this.Height }; + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BasicTestPatternProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BasicTestPatternProvider.cs new file mode 100644 index 0000000000..47bb22aeb3 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BasicTestPatternProvider.cs @@ -0,0 +1,65 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Tests +{ + public abstract partial class TestImageProvider + { + private class BasicTestPatternProvider : BlankProvider + { + public BasicTestPatternProvider(int width, int height) + : base(width, height) + { + } + + /// + /// This parameterless constructor is needed for xUnit deserialization + /// + public BasicTestPatternProvider() + { + } + + public override string SourceFileOrDescription => TestUtils.AsInvariantString($"BasicTestPattern{this.Width}x{this.Height}"); + + public override Image GetImage() + { + var result = new Image(this.Width, this.Height); + + TPixel topLeftColor = NamedColors.Red; + TPixel topRightColor = NamedColors.Green; + TPixel bottomLeftColor = NamedColors.Blue; + + // Transparent purple: + TPixel bottomRightColor = default; + bottomRightColor.FromVector4(new Vector4(1f, 0f, 1f, 0.5f)); + + int midY = this.Height / 2; + int midX = this.Width / 2; + + for (int y = 0; y < midY; y++) + { + Span row = result.GetPixelRowSpan(y); + + row.Slice(0, midX).Fill(topLeftColor); + row.Slice(midX, this.Width-midX).Fill(topRightColor); + } + + for (int y = midY; y < this.Height; y++) + { + Span row = result.GetPixelRowSpan(y); + + row.Slice(0, midX).Fill(bottomLeftColor); + row.Slice(midX, this.Width-midX).Fill(bottomRightColor); + } + + return result; + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs index 7821d0b51a..1275e522f8 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs @@ -20,6 +20,9 @@ namespace SixLabors.ImageSharp.Tests this.Height = height; } + /// + /// This parameterless constructor is needed for xUnit deserialization + /// public BlankProvider() { this.Width = 100; diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs index d68c37a768..1ff95f60d0 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs @@ -35,6 +35,9 @@ namespace SixLabors.ImageSharp.Tests this.a = a; } + /// + /// This parameterless constructor is needed for xUnit deserialization + /// public SolidProvider() : base() { diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs index 5b5e4740a3..52f66a78b5 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs @@ -44,6 +44,12 @@ namespace SixLabors.ImageSharp.Tests public string MethodName { get; private set; } public string OutputSubfolderName { get; private set; } + public static TestImageProvider BasicTestPattern(int width, + int height, + MethodInfo testMethod = null, + PixelTypes pixelTypeOverride = PixelTypes.Undefined) + => new BasicTestPatternProvider(width, height).Init(testMethod, pixelTypeOverride); + public static TestImageProvider TestPattern( int width, int height, diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs index 17e5369d48..336cdbf11f 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Net.Mime; using System.Numerics; using SixLabors.ImageSharp.Memory; @@ -25,8 +26,10 @@ namespace SixLabors.ImageSharp.Tests { } + /// + /// This parameterless constructor is needed for xUnit deserialization + /// public TestPatternProvider() - : base() { } diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs index cac7828e96..1bee34f1a7 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs @@ -325,6 +325,20 @@ namespace SixLabors.ImageSharp.Tests } } + [Theory] + [WithBasicTestPatternImages(50, 100, PixelTypes.Rgba32)] + [WithBasicTestPatternImages(49,17, PixelTypes.Rgba32)] + [WithBasicTestPatternImages(20, 10, PixelTypes.Rgba32)] + public void Use_WithBasicTestPatternImages(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image img = provider.GetImage()) + { + img.DebugSave(provider); + } + } + + public static readonly TheoryData BasicData = new TheoryData() { TestImageProvider.Blank(10, 20), From 1f12fd810459de32ee68923d9d7ac3befc665411 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 8 Apr 2019 00:41:18 +0200 Subject: [PATCH 051/223] drop parallelism in ResizeProcessor for simplicity --- .../Transforms/Resize/ResizeProcessor.cs | 72 ++++++++----------- 1 file changed, 29 insertions(+), 43 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs index 198427d284..37d0ab413a 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.Collections.Generic; using System.Linq; using System.Numerics; @@ -244,63 +245,48 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms // First process the columns. Since we are not using multiple threads startY and endY // are the upper and lower bounds of the source rectangle. using (Buffer2D firstPassPixelsTransposed = source.MemoryAllocator.Allocate2D(sourceHeight, width)) + using (IMemoryOwner tempBuffer = source.MemoryAllocator.Allocate(Math.Max(source.Width, width))) { firstPassPixelsTransposed.MemorySource.Clear(); - var processColsRect = new Rectangle(0, 0, source.Width, sourceRectangle.Bottom); + for (int y = 0; y < sourceRectangle.Bottom; y++) + { + Span sourceRow = source.GetPixelRowSpan(y).Slice(sourceX); + Span tempRowSpan = tempBuffer.GetSpan().Slice(sourceX, source.Width - sourceX); - ParallelHelper.IterateRowsWithTempBuffer( - processColsRect, - configuration, - (rows, tempRowBuffer) => - { - for (int y = rows.Min; y < rows.Max; y++) - { - Span sourceRow = source.GetPixelRowSpan(y).Slice(sourceX); - Span tempRowSpan = tempRowBuffer.Span.Slice(sourceX); - - PixelOperations.Instance.ToVector4(configuration, sourceRow, tempRowSpan, conversionModifiers); - - ref Vector4 firstPassBaseRef = ref firstPassPixelsTransposed.Span[y]; + PixelOperations.Instance.ToVector4(configuration, sourceRow, tempRowSpan, conversionModifiers); - for (int x = minX; x < maxX; x++) - { - ResizeKernel kernel = this.horizontalKernelMap.GetKernel(x - startX); - Unsafe.Add(ref firstPassBaseRef, x * sourceHeight) = kernel.Convolve(tempRowSpan); - } - } - }); + ref Vector4 firstPassBaseRef = ref firstPassPixelsTransposed.Span[y]; - var processRowsRect = Rectangle.FromLTRB(0, minY, width, maxY); + for (int x = minX; x < maxX; x++) + { + ResizeKernel kernel = this.horizontalKernelMap.GetKernel(x - startX); + Unsafe.Add(ref firstPassBaseRef, x * sourceHeight) = kernel.Convolve(tempRowSpan); + } + } // Now process the rows. - ParallelHelper.IterateRowsWithTempBuffer( - processRowsRect, - configuration, - (rows, tempRowBuffer) => - { - Span tempRowSpan = tempRowBuffer.Span; + Span tempColSpan = tempBuffer.GetSpan().Slice(0, width); - for (int y = rows.Min; y < rows.Max; y++) - { - // Ensure offsets are normalized for cropping and padding. - ResizeKernel kernel = this.verticalKernelMap.GetKernel(y - startY); + for (int y = minY; y < maxY; y++) + { + // Ensure offsets are normalized for cropping and padding. + ResizeKernel kernel = this.verticalKernelMap.GetKernel(y - startY); - ref Vector4 tempRowBase = ref MemoryMarshal.GetReference(tempRowSpan); + ref Vector4 tempRowBase = ref MemoryMarshal.GetReference(tempColSpan); - for (int x = 0; x < width; x++) - { - Span firstPassColumn = firstPassPixelsTransposed.GetRowSpan(x).Slice(sourceY); + for (int x = 0; x < width; x++) + { + Span firstPassColumn = firstPassPixelsTransposed.GetRowSpan(x).Slice(sourceY); - // Destination color components - Unsafe.Add(ref tempRowBase, x) = kernel.Convolve(firstPassColumn); - } + // Destination color components + Unsafe.Add(ref tempRowBase, x) = kernel.Convolve(firstPassColumn); + } - Span targetRowSpan = destination.GetPixelRowSpan(y); + Span targetRowSpan = destination.GetPixelRowSpan(y); - PixelOperations.Instance.FromVector4Destructive(configuration, tempRowSpan, targetRowSpan, conversionModifiers); - } - }); + PixelOperations.Instance.FromVector4Destructive(configuration, tempColSpan, targetRowSpan, conversionModifiers); + } } } From 0786ff70d3ecca6a1ed31354cd04816865753681 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 8 Apr 2019 00:59:24 +0200 Subject: [PATCH 052/223] ResizeRectangle -> TargetRectangle --- .../Transforms/Resize/ResizeProcessor.cs | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs index 37d0ab413a..cb1ab2d546 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs @@ -66,7 +66,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms this.Sampler = options.Sampler; this.Width = size.Width; this.Height = size.Height; - this.ResizeRectangle = rectangle; + this.TargetRectangle = rectangle; this.Compand = options.Compand; } @@ -89,11 +89,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// The target width. /// The target height. /// The source image size - /// + /// /// The structure that specifies the portion of the target image object to draw to. /// /// Whether to compress or expand individual pixel color values on processing. - public ResizeProcessor(IResampler sampler, int width, int height, Size sourceSize, Rectangle resizeRectangle, bool compand) + public ResizeProcessor(IResampler sampler, int width, int height, Size sourceSize, Rectangle targetRectangle, bool compand) { Guard.NotNull(sampler, nameof(sampler)); @@ -104,13 +104,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms if (width == 0 && height > 0) { width = (int)MathF.Max(min, MathF.Round(sourceSize.Width * height / (float)sourceSize.Height)); - resizeRectangle.Width = width; + targetRectangle.Width = width; } if (height == 0 && width > 0) { height = (int)MathF.Max(min, MathF.Round(sourceSize.Height * width / (float)sourceSize.Width)); - resizeRectangle.Height = height; + targetRectangle.Height = height; } Guard.MustBeGreaterThan(width, 0, nameof(width)); @@ -119,7 +119,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms this.Sampler = sampler; this.Width = width; this.Height = height; - this.ResizeRectangle = resizeRectangle; + this.TargetRectangle = targetRectangle; this.Compand = compand; } @@ -141,7 +141,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// /// Gets the resize rectangle. /// - public Rectangle ResizeRectangle { get; } + public Rectangle TargetRectangle { get; } /// /// Gets a value indicating whether to compress or expand individual pixel color values on processing. @@ -167,13 +167,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms MemoryAllocator memoryAllocator = source.GetMemoryAllocator(); this.horizontalKernelMap = ResizeKernelMap.Calculate( this.Sampler, - this.ResizeRectangle.Width, + this.TargetRectangle.Width, sourceRectangle.Width, memoryAllocator); this.verticalKernelMap = ResizeKernelMap.Calculate( this.Sampler, - this.ResizeRectangle.Height, + this.TargetRectangle.Height, sourceRectangle.Height, memoryAllocator); } @@ -183,7 +183,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms protected override void OnFrameApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) { // Handle resize dimensions identical to the original - if (source.Width == destination.Width && source.Height == destination.Height && sourceRectangle == this.ResizeRectangle) + if (source.Width == destination.Width && source.Height == destination.Height && sourceRectangle == this.TargetRectangle) { // The cloned will be blank here copy all the pixel data over source.GetPixelSpan().CopyTo(destination.GetPixelSpan()); @@ -194,10 +194,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms int height = this.Height; int sourceX = sourceRectangle.X; int sourceY = sourceRectangle.Y; - int startY = this.ResizeRectangle.Y; - int endY = this.ResizeRectangle.Bottom; - int startX = this.ResizeRectangle.X; - int endX = this.ResizeRectangle.Right; + int startY = this.TargetRectangle.Y; + int endY = this.TargetRectangle.Bottom; + int startX = this.TargetRectangle.X; + int endX = this.TargetRectangle.Right; int minX = Math.Max(0, startX); int maxX = Math.Min(width, endX); @@ -209,8 +209,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY); // Scaling factors - float widthFactor = sourceRectangle.Width / (float)this.ResizeRectangle.Width; - float heightFactor = sourceRectangle.Height / (float)this.ResizeRectangle.Height; + float widthFactor = sourceRectangle.Width / (float)this.TargetRectangle.Width; + float heightFactor = sourceRectangle.Height / (float)this.TargetRectangle.Height; ParallelHelper.IterateRows( workingRect, From fca697206ab49829f9da59cb5f65321922a3e04f Mon Sep 17 00:00:00 2001 From: Jason Nelson Date: Fri, 12 Apr 2019 23:37:17 -0500 Subject: [PATCH 053/223] Update metadata names (#879) --- .../Common/Helpers/UnitConverter.cs | 4 +- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 26 ++-- src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs | 26 ++-- src/ImageSharp/Formats/Bmp/BmpFormat.cs | 4 +- src/ImageSharp/Formats/Bmp/BmpMetaData.cs | 12 +- src/ImageSharp/Formats/Gif/GifDecoder.cs | 2 +- src/ImageSharp/Formats/Gif/GifDecoderCore.cs | 34 ++--- src/ImageSharp/Formats/Gif/GifEncoderCore.cs | 60 ++++---- src/ImageSharp/Formats/Gif/GifFormat.cs | 6 +- .../Formats/Gif/GifFrameMetaData.cs | 12 +- src/ImageSharp/Formats/Gif/GifMetaData.cs | 12 +- .../Formats/Gif/IGifDecoderOptions.cs | 2 +- src/ImageSharp/Formats/IImageFormat.cs | 24 ++-- .../Jpeg/Components/Decoder/JFifMarker.cs | 2 +- .../Formats/Jpeg/JpegDecoderCore.cs | 44 +++--- .../Formats/Jpeg/JpegEncoderCore.cs | 30 ++-- src/ImageSharp/Formats/Jpeg/JpegFormat.cs | 4 +- src/ImageSharp/Formats/Jpeg/JpegMetaData.cs | 12 +- .../Formats/Png/Chunks/PhysicalChunkData.cs | 4 +- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 128 +++++++++--------- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 54 ++++---- src/ImageSharp/Formats/Png/PngFormat.cs | 4 +- src/ImageSharp/Formats/Png/PngMetaData.cs | 12 +- src/ImageSharp/IImageInfo.cs | 4 +- src/ImageSharp/Image.Decode.cs | 6 +- src/ImageSharp/Image.WrapMemory.cs | 18 +-- src/ImageSharp/ImageFrameCollection.cs | 4 +- src/ImageSharp/ImageFrame{TPixel}.cs | 42 +++--- src/ImageSharp/ImageInfo.cs | 10 +- src/ImageSharp/Image{TPixel}.cs | 28 ++-- src/ImageSharp/MetaData/FrameDecodingMode.cs | 2 +- src/ImageSharp/MetaData/ImageFrameMetaData.cs | 42 +++--- src/ImageSharp/MetaData/ImageMetaData.cs | 44 +++--- src/ImageSharp/MetaData/ImageProperty.cs | 2 +- .../MetaData/PixelResolutionUnit.cs | 2 +- .../MetaData/Profiles/Exif/ExifConstants.cs | 2 +- .../MetaData/Profiles/Exif/ExifDataType.cs | 2 +- .../MetaData/Profiles/Exif/ExifParts.cs | 2 +- .../MetaData/Profiles/Exif/ExifProfile.cs | 12 +- .../MetaData/Profiles/Exif/ExifReader.cs | 2 +- .../MetaData/Profiles/Exif/ExifTag.cs | 2 +- .../Exif/ExifTagDescriptionAttribute.cs | 2 +- .../MetaData/Profiles/Exif/ExifTags.cs | 4 +- .../MetaData/Profiles/Exif/ExifValue.cs | 2 +- .../MetaData/Profiles/Exif/ExifWriter.cs | 2 +- .../Profiles/ICC/Curves/IccCurveSegment.cs | 2 +- .../ICC/Curves/IccFormulaCurveElement.cs | 2 +- .../ICC/Curves/IccOneDimensionalCurve.cs | 2 +- .../Profiles/ICC/Curves/IccParametricCurve.cs | 2 +- .../Profiles/ICC/Curves/IccResponseCurve.cs | 2 +- .../ICC/Curves/IccSampledCurveElement.cs | 2 +- .../ICC/DataReader/IccDataReader.Curves.cs | 2 +- .../ICC/DataReader/IccDataReader.Lut.cs | 2 +- .../ICC/DataReader/IccDataReader.Matrix.cs | 2 +- .../IccDataReader.MultiProcessElement.cs | 2 +- .../DataReader/IccDataReader.NonPrimitives.cs | 2 +- .../DataReader/IccDataReader.Primitives.cs | 2 +- .../DataReader/IccDataReader.TagDataEntry.cs | 2 +- .../Profiles/ICC/DataReader/IccDataReader.cs | 2 +- .../ICC/DataWriter/IccDataWriter.Curves.cs | 2 +- .../ICC/DataWriter/IccDataWriter.Lut.cs | 2 +- .../ICC/DataWriter/IccDataWriter.Matrix.cs | 2 +- .../IccDataWriter.MultiProcessElement.cs | 2 +- .../DataWriter/IccDataWriter.NonPrimitives.cs | 2 +- .../DataWriter/IccDataWriter.Primitives.cs | 2 +- .../DataWriter/IccDataWriter.TagDataEntry.cs | 2 +- .../Profiles/ICC/DataWriter/IccDataWriter.cs | 2 +- .../Profiles/ICC/Enums/IccClutDataType.cs | 2 +- .../Profiles/ICC/Enums/IccColorSpaceType.cs | 2 +- .../Profiles/ICC/Enums/IccColorantEncoding.cs | 2 +- .../ICC/Enums/IccCurveMeasurementEncodings.cs | 2 +- .../ICC/Enums/IccCurveSegmentSignature.cs | 2 +- .../Profiles/ICC/Enums/IccDataType.cs | 2 +- .../Profiles/ICC/Enums/IccDeviceAttribute.cs | 2 +- .../Profiles/ICC/Enums/IccFormulaCurveType.cs | 2 +- .../ICC/Enums/IccMeasurementGeometry.cs | 2 +- .../Enums/IccMultiProcessElementSignature.cs | 2 +- .../ICC/Enums/IccParametricCurveType.cs | 2 +- .../ICC/Enums/IccPrimaryPlatformType.cs | 2 +- .../Profiles/ICC/Enums/IccProfileClass.cs | 2 +- .../Profiles/ICC/Enums/IccProfileFlag.cs | 2 +- .../Profiles/ICC/Enums/IccProfileTag.cs | 2 +- .../Profiles/ICC/Enums/IccRenderingIntent.cs | 2 +- .../Profiles/ICC/Enums/IccScreeningFlag.cs | 2 +- .../ICC/Enums/IccScreeningSpotType.cs | 2 +- .../Profiles/ICC/Enums/IccSignatureName.cs | 2 +- .../ICC/Enums/IccStandardIlluminant.cs | 2 +- .../Profiles/ICC/Enums/IccStandardObserver.cs | 2 +- .../Profiles/ICC/Enums/IccTypeSignature.cs | 2 +- .../Exceptions/InvalidIccProfileException.cs | 2 +- .../MetaData/Profiles/ICC/IccProfile.cs | 2 +- .../MetaData/Profiles/ICC/IccProfileHeader.cs | 2 +- .../MetaData/Profiles/ICC/IccReader.cs | 2 +- .../MetaData/Profiles/ICC/IccTagDataEntry.cs | 2 +- .../MetaData/Profiles/ICC/IccWriter.cs | 2 +- .../IccBAcsProcessElement.cs | 2 +- .../IccClutProcessElement.cs | 2 +- .../IccCurveSetProcessElement.cs | 2 +- .../IccEAcsProcessElement.cs | 2 +- .../IccMatrixProcessElement.cs | 2 +- .../IccMultiProcessElement.cs | 2 +- .../IccChromaticityTagDataEntry.cs | 2 +- .../IccColorantOrderTagDataEntry.cs | 2 +- .../IccColorantTableTagDataEntry.cs | 2 +- .../TagDataEntries/IccCrdInfoTagDataEntry.cs | 2 +- .../TagDataEntries/IccCurveTagDataEntry.cs | 2 +- .../ICC/TagDataEntries/IccDataTagDataEntry.cs | 2 +- .../TagDataEntries/IccDateTimeTagDataEntry.cs | 2 +- .../IccFix16ArrayTagDataEntry.cs | 2 +- .../TagDataEntries/IccLut16TagDataEntry.cs | 2 +- .../ICC/TagDataEntries/IccLut8TagDataEntry.cs | 2 +- .../TagDataEntries/IccLutAToBTagDataEntry.cs | 2 +- .../TagDataEntries/IccLutBToATagDataEntry.cs | 2 +- .../IccMeasurementTagDataEntry.cs | 2 +- .../IccMultiLocalizedUnicodeTagDataEntry.cs | 2 +- .../IccMultiProcessElementsTagDataEntry.cs | 2 +- .../IccNamedColor2TagDataEntry.cs | 2 +- .../IccParametricCurveTagDataEntry.cs | 2 +- .../IccProfileSequenceDescTagDataEntry.cs | 2 +- ...ccProfileSequenceIdentifierTagDataEntry.cs | 2 +- .../IccResponseCurveSet16TagDataEntry.cs | 2 +- .../IccScreeningTagDataEntry.cs | 2 +- .../IccSignatureTagDataEntry.cs | 2 +- .../IccTextDescriptionTagDataEntry.cs | 2 +- .../ICC/TagDataEntries/IccTextTagDataEntry.cs | 2 +- .../IccUFix16ArrayTagDataEntry.cs | 2 +- .../IccUInt16ArrayTagDataEntry.cs | 2 +- .../IccUInt32ArrayTagDataEntry.cs | 2 +- .../IccUInt64ArrayTagDataEntry.cs | 2 +- .../IccUInt8ArrayTagDataEntry.cs | 2 +- .../TagDataEntries/IccUcrBgTagDataEntry.cs | 2 +- .../TagDataEntries/IccUnknownTagDataEntry.cs | 2 +- .../IccViewingConditionsTagDataEntry.cs | 2 +- .../ICC/TagDataEntries/IccXyzTagDataEntry.cs | 2 +- .../MetaData/Profiles/ICC/Various/IccClut.cs | 2 +- .../ICC/Various/IccColorantTableEntry.cs | 2 +- .../ICC/Various/IccLocalizedString.cs | 2 +- .../MetaData/Profiles/ICC/Various/IccLut.cs | 2 +- .../Profiles/ICC/Various/IccNamedColor.cs | 2 +- .../Profiles/ICC/Various/IccPositionNumber.cs | 2 +- .../ICC/Various/IccProfileDescription.cs | 2 +- .../Profiles/ICC/Various/IccProfileId.cs | 2 +- .../Various/IccProfileSequenceIdentifier.cs | 2 +- .../Profiles/ICC/Various/IccResponseNumber.cs | 2 +- .../ICC/Various/IccScreeningChannel.cs | 2 +- .../Profiles/ICC/Various/IccTagTableEntry.cs | 2 +- .../Profiles/ICC/Various/IccVersion.cs | 2 +- .../Transforms/AffineTransformProcessor.cs | 4 +- .../Transforms/AutoOrientProcessor.cs | 10 +- .../Processors/Transforms/CropProcessor.cs | 4 +- .../ProjectiveTransformProcessor.cs | 4 +- .../Transforms/Resize/ResizeProcessor.cs | 4 +- .../Processors/Transforms/RotateProcessor.cs | 4 +- .../Transforms/TransformProcessorHelpers.cs | 4 +- .../Formats/Bmp/BmpDecoderTests.cs | 4 +- .../Formats/Bmp/BmpEncoderTests.cs | 6 +- .../Formats/Bmp/BmpMetaDataTests.cs | 4 +- .../Formats/GeneralFormatTests.cs | 4 +- .../Formats/Gif/GifDecoderTests.cs | 20 +-- .../Formats/Gif/GifEncoderTests.cs | 34 ++--- .../Formats/Gif/GifFrameMetaDataTests.cs | 4 +- .../Formats/Gif/GifMetaDataTests.cs | 4 +- .../Formats/Jpg/JFifMarkerTests.cs | 2 +- .../Formats/Jpg/JpegDecoderTests.MetaData.cs | 34 ++--- .../Formats/Jpg/JpegEncoderTests.cs | 6 +- .../Formats/Jpg/JpegMetaDataTests.cs | 4 +- .../Formats/Png/PngDecoderTests.cs | 18 +-- .../Formats/Png/PngEncoderTests.cs | 10 +- .../Formats/Png/PngMetaDataTests.cs | 4 +- .../Image/ImageTests.WrapMemory.cs | 6 +- tests/ImageSharp.Tests/Image/ImageTests.cs | 6 +- tests/ImageSharp.Tests/ImageInfoTests.cs | 6 +- .../MetaData/ImageFrameMetaDataTests.cs | 16 +-- .../MetaData/ImageMetaDataTests.cs | 32 ++--- .../MetaData/ImagePropertyTests.cs | 2 +- .../Profiles/Exif/ExifProfileTests.cs | 104 +++++++------- .../MetaData/Profiles/Exif/ExifReaderTests.cs | 2 +- .../Exif/ExifTagDescriptionAttributeTests.cs | 2 +- .../MetaData/Profiles/Exif/ExifValueTests.cs | 4 +- .../DataReader/IccDataReader.CurvesTests.cs | 2 +- .../ICC/DataReader/IccDataReader.LutTests.cs | 2 +- .../DataReader/IccDataReader.MatrixTests.cs | 2 +- .../IccDataReader.MultiProcessElementTests.cs | 2 +- .../IccDataReader.NonPrimitivesTests.cs | 2 +- .../IccDataReader.PrimitivesTests.cs | 2 +- .../IccDataReader.TagDataEntryTests.cs | 2 +- .../ICC/DataReader/IccDataReaderTests.cs | 2 +- .../DataWriter/IccDataWriter.CurvesTests.cs | 2 +- .../ICC/DataWriter/IccDataWriter.LutTests.cs | 2 +- .../DataWriter/IccDataWriter.MatrixTests.cs | 2 +- .../IccDataWriter.MultiProcessElementTests.cs | 2 +- .../IccDataWriter.NonPrimitivesTests.cs | 2 +- .../IccDataWriter.PrimitivesTests.cs | 2 +- .../IccDataWriter.TagDataEntryTests.cs | 2 +- .../ICC/DataWriter/IccDataWriterTests.cs | 2 +- .../MetaData/Profiles/ICC/IccProfileTests.cs | 2 +- .../MetaData/Profiles/ICC/IccReaderTests.cs | 2 +- .../MetaData/Profiles/ICC/IccWriterTests.cs | 2 +- .../Profiles/ICC/Various/IccProfileIdTests.cs | 2 +- .../Processors/Transforms/AutoOrientTests.cs | 8 +- .../Transforms/TransformsHelpersTest.cs | 4 +- .../TestDataIcc/IccTestDataCurves.cs | 2 +- .../TestDataIcc/IccTestDataLut.cs | 2 +- .../IccTestDataMultiProcessElements.cs | 2 +- .../TestDataIcc/IccTestDataNonPrimitives.cs | 2 +- .../TestDataIcc/IccTestDataProfiles.cs | 2 +- .../TestDataIcc/IccTestDataTagDataEntry.cs | 2 +- .../SystemDrawingReferenceDecoder.cs | 4 +- 208 files changed, 699 insertions(+), 699 deletions(-) diff --git a/src/ImageSharp/Common/Helpers/UnitConverter.cs b/src/ImageSharp/Common/Helpers/UnitConverter.cs index c8b25bf564..75dbb032c5 100644 --- a/src/ImageSharp/Common/Helpers/UnitConverter.cs +++ b/src/ImageSharp/Common/Helpers/UnitConverter.cs @@ -2,8 +2,8 @@ // Licensed under the Apache License, Version 2.0. using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.MetaData; -using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using SixLabors.ImageSharp.Metadata; +using SixLabors.ImageSharp.Metadata.Profiles.Exif; namespace SixLabors.ImageSharp.Common.Helpers { diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index d33ada2864..b52c3754e2 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -9,7 +9,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Memory; @@ -66,12 +66,12 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// /// The metadata. /// - private ImageMetaData metaData; + private ImageMetadata metadata; /// /// The bmp specific metadata. /// - private BmpMetaData bmpMetaData; + private BmpMetadata bmpMetadata; /// /// The file header containing general information. @@ -116,7 +116,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp { int bytesPerColorMapEntry = this.ReadImageHeaders(stream, out bool inverted, out byte[] palette); - var image = new Image(this.configuration, this.infoHeader.Width, this.infoHeader.Height, this.metaData); + var image = new Image(this.configuration, this.infoHeader.Width, this.infoHeader.Height, this.metadata); Buffer2D pixels = image.GetRootFramePixelBuffer(); @@ -125,7 +125,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp case BmpCompression.RGB: if (this.infoHeader.BitsPerPixel == 32) { - if (this.bmpMetaData.InfoHeaderType == BmpInfoHeaderType.WinVersion3) + if (this.bmpMetadata.InfoHeaderType == BmpInfoHeaderType.WinVersion3) { this.ReadRgb32Slow(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); } @@ -188,7 +188,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp public IImageInfo Identify(Stream stream) { this.ReadImageHeaders(stream, out _, out _); - return new ImageInfo(new PixelTypeInfo(this.infoHeader.BitsPerPixel), this.infoHeader.Width, this.infoHeader.Height, this.metaData); + return new ImageInfo(new PixelTypeInfo(this.infoHeader.BitsPerPixel), this.infoHeader.Width, this.infoHeader.Height, this.metadata); } /// @@ -999,7 +999,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp } // Resolution is stored in PPM. - var meta = new ImageMetaData + var meta = new ImageMetadata { ResolutionUnits = PixelResolutionUnit.PixelsPerMeter }; @@ -1011,21 +1011,21 @@ namespace SixLabors.ImageSharp.Formats.Bmp else { // Convert default metadata values to PPM. - meta.HorizontalResolution = Math.Round(UnitConverter.InchToMeter(ImageMetaData.DefaultHorizontalResolution)); - meta.VerticalResolution = Math.Round(UnitConverter.InchToMeter(ImageMetaData.DefaultVerticalResolution)); + meta.HorizontalResolution = Math.Round(UnitConverter.InchToMeter(ImageMetadata.DefaultHorizontalResolution)); + meta.VerticalResolution = Math.Round(UnitConverter.InchToMeter(ImageMetadata.DefaultVerticalResolution)); } - this.metaData = meta; + this.metadata = meta; short bitsPerPixel = this.infoHeader.BitsPerPixel; - this.bmpMetaData = this.metaData.GetFormatMetaData(BmpFormat.Instance); - this.bmpMetaData.InfoHeaderType = infoHeaderType; + this.bmpMetadata = this.metadata.GetFormatMetadata(BmpFormat.Instance); + this.bmpMetadata.InfoHeaderType = infoHeaderType; // We can only encode at these bit rates so far. if (bitsPerPixel.Equals((short)BmpBitsPerPixel.Pixel24) || bitsPerPixel.Equals((short)BmpBitsPerPixel.Pixel32)) { - this.bmpMetaData.BitsPerPixel = (BmpBitsPerPixel)bitsPerPixel; + this.bmpMetadata.BitsPerPixel = (BmpBitsPerPixel)bitsPerPixel; } // skip the remaining header because we can't read those parts diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index 27a38bc0d1..ad526f6689 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -7,7 +7,7 @@ using System.IO; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Memory; @@ -73,9 +73,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp Guard.NotNull(stream, nameof(stream)); this.configuration = image.GetConfiguration(); - ImageMetaData metaData = image.MetaData; - BmpMetaData bmpMetaData = metaData.GetFormatMetaData(BmpFormat.Instance); - this.bitsPerPixel = this.bitsPerPixel ?? bmpMetaData.BitsPerPixel; + ImageMetadata metadata = image.Metadata; + BmpMetadata bmpMetadata = metadata.GetFormatMetadata(BmpFormat.Instance); + this.bitsPerPixel = this.bitsPerPixel ?? bmpMetadata.BitsPerPixel; short bpp = (short)this.bitsPerPixel; int bytesPerLine = 4 * (((image.Width * bpp) + 31) / 32); @@ -85,27 +85,27 @@ namespace SixLabors.ImageSharp.Formats.Bmp int hResolution = 0; int vResolution = 0; - if (metaData.ResolutionUnits != PixelResolutionUnit.AspectRatio) + if (metadata.ResolutionUnits != PixelResolutionUnit.AspectRatio) { - if (metaData.HorizontalResolution > 0 && metaData.VerticalResolution > 0) + if (metadata.HorizontalResolution > 0 && metadata.VerticalResolution > 0) { - switch (metaData.ResolutionUnits) + switch (metadata.ResolutionUnits) { case PixelResolutionUnit.PixelsPerInch: - hResolution = (int)Math.Round(UnitConverter.InchToMeter(metaData.HorizontalResolution)); - vResolution = (int)Math.Round(UnitConverter.InchToMeter(metaData.VerticalResolution)); + hResolution = (int)Math.Round(UnitConverter.InchToMeter(metadata.HorizontalResolution)); + vResolution = (int)Math.Round(UnitConverter.InchToMeter(metadata.VerticalResolution)); break; case PixelResolutionUnit.PixelsPerCentimeter: - hResolution = (int)Math.Round(UnitConverter.CmToMeter(metaData.HorizontalResolution)); - vResolution = (int)Math.Round(UnitConverter.CmToMeter(metaData.VerticalResolution)); + hResolution = (int)Math.Round(UnitConverter.CmToMeter(metadata.HorizontalResolution)); + vResolution = (int)Math.Round(UnitConverter.CmToMeter(metadata.VerticalResolution)); break; case PixelResolutionUnit.PixelsPerMeter: - hResolution = (int)Math.Round(metaData.HorizontalResolution); - vResolution = (int)Math.Round(metaData.VerticalResolution); + hResolution = (int)Math.Round(metadata.HorizontalResolution); + vResolution = (int)Math.Round(metadata.VerticalResolution); break; } diff --git a/src/ImageSharp/Formats/Bmp/BmpFormat.cs b/src/ImageSharp/Formats/Bmp/BmpFormat.cs index a5eaab8ebf..056fbe8406 100644 --- a/src/ImageSharp/Formats/Bmp/BmpFormat.cs +++ b/src/ImageSharp/Formats/Bmp/BmpFormat.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// /// Registers the image encoders, decoders and mime type detectors for the bmp format. /// - public sealed class BmpFormat : IImageFormat + public sealed class BmpFormat : IImageFormat { private BmpFormat() { @@ -32,6 +32,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp public IEnumerable FileExtensions => BmpConstants.FileExtensions; /// - public BmpMetaData CreateDefaultFormatMetaData() => new BmpMetaData(); + public BmpMetadata CreateDefaultFormatMetadata() => new BmpMetadata(); } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Bmp/BmpMetaData.cs b/src/ImageSharp/Formats/Bmp/BmpMetaData.cs index 2d86856177..83d4eefe40 100644 --- a/src/ImageSharp/Formats/Bmp/BmpMetaData.cs +++ b/src/ImageSharp/Formats/Bmp/BmpMetaData.cs @@ -6,20 +6,20 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// /// Provides Bmp specific metadata information for the image. /// - public class BmpMetaData : IDeepCloneable + public class BmpMetadata : IDeepCloneable { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public BmpMetaData() + public BmpMetadata() { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The metadata to create an instance from. - private BmpMetaData(BmpMetaData other) + private BmpMetadata(BmpMetadata other) { this.BitsPerPixel = other.BitsPerPixel; this.InfoHeaderType = other.InfoHeaderType; @@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp public BmpBitsPerPixel BitsPerPixel { get; set; } = BmpBitsPerPixel.Pixel24; /// - public IDeepCloneable DeepClone() => new BmpMetaData(this); + public IDeepCloneable DeepClone() => new BmpMetadata(this); // TODO: Colors used once we support encoding palette bmps. } diff --git a/src/ImageSharp/Formats/Gif/GifDecoder.cs b/src/ImageSharp/Formats/Gif/GifDecoder.cs index 42c76d6400..6af75f2d0f 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoder.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoder.cs @@ -3,7 +3,7 @@ using System.IO; using System.Text; -using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Gif diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index bfa91416aa..e16ecb42e3 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -8,7 +8,7 @@ using System.Runtime.InteropServices; using System.Text; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Memory; using SixLabors.Primitives; @@ -63,12 +63,12 @@ namespace SixLabors.ImageSharp.Formats.Gif /// /// The abstract metadata. /// - private ImageMetaData metaData; + private ImageMetadata metadata; /// /// The gif specific metadata. /// - private GifMetaData gifMetaData; + private GifMetadata gifMetadata; /// /// Initializes a new instance of the class. @@ -223,7 +223,7 @@ namespace SixLabors.ImageSharp.Formats.Gif new PixelTypeInfo(this.logicalScreenDescriptor.BitsPerPixel), this.logicalScreenDescriptor.Width, this.logicalScreenDescriptor.Height, - this.metaData); + this.metadata); } /// @@ -276,7 +276,7 @@ namespace SixLabors.ImageSharp.Formats.Gif if (subBlockSize == GifConstants.NetscapeLoopingSubBlockSize) { this.stream.Read(this.buffer, 0, GifConstants.NetscapeLoopingSubBlockSize); - this.gifMetaData.RepeatCount = GifNetscapeLoopingApplicationExtension.Parse(this.buffer.AsSpan(1)).RepeatCount; + this.gifMetadata.RepeatCount = GifNetscapeLoopingApplicationExtension.Parse(this.buffer.AsSpan(1)).RepeatCount; this.stream.Skip(1); // Skip the terminator. return; } @@ -334,7 +334,7 @@ namespace SixLabors.ImageSharp.Formats.Gif { this.stream.Read(commentsBuffer.Array, 0, length); string comments = this.TextEncoding.GetString(commentsBuffer.Array, 0, length); - this.metaData.Properties.Add(new ImageProperty(GifConstants.Comments, comments)); + this.metadata.Properties.Add(new ImageProperty(GifConstants.Comments, comments)); } } } @@ -416,9 +416,9 @@ namespace SixLabors.ImageSharp.Formats.Gif if (previousFrame is null) { // This initializes the image to become fully transparent because the alpha channel is zero. - image = new Image(this.configuration, imageWidth, imageHeight, this.metaData); + image = new Image(this.configuration, imageWidth, imageHeight, this.metadata); - this.SetFrameMetaData(image.Frames.RootFrame.MetaData); + this.SetFrameMetadata(image.Frames.RootFrame.Metadata); imageFrame = image.Frames.RootFrame; } @@ -431,7 +431,7 @@ namespace SixLabors.ImageSharp.Formats.Gif currentFrame = image.Frames.AddFrame(previousFrame); // This clones the frame and adds it the collection - this.SetFrameMetaData(currentFrame.MetaData); + this.SetFrameMetadata(currentFrame.Metadata); imageFrame = currentFrame; @@ -549,11 +549,11 @@ namespace SixLabors.ImageSharp.Formats.Gif /// /// Sets the frames metadata. /// - /// The meta data. + /// The metadata. [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void SetFrameMetaData(ImageFrameMetaData meta) + private void SetFrameMetadata(ImageFrameMetadata meta) { - GifFrameMetaData gifMeta = meta.GetFormatMetaData(GifFormat.Instance); + GifFrameMetadata gifMeta = meta.GetFormatMetadata(GifFormat.Instance); if (this.graphicsControlExtension.DelayTime > 0) { gifMeta.FrameDelay = this.graphicsControlExtension.DelayTime; @@ -586,7 +586,7 @@ namespace SixLabors.ImageSharp.Formats.Gif this.stream.Skip(6); this.ReadLogicalScreenDescriptor(); - var meta = new ImageMetaData(); + var meta = new ImageMetadata(); // The Pixel Aspect Ratio is defined to be the quotient of the pixel's // width over its height. The value range in this field allows @@ -614,16 +614,16 @@ namespace SixLabors.ImageSharp.Formats.Gif } } - this.metaData = meta; - this.gifMetaData = meta.GetFormatMetaData(GifFormat.Instance); - this.gifMetaData.ColorTableMode = this.logicalScreenDescriptor.GlobalColorTableFlag + this.metadata = meta; + this.gifMetadata = meta.GetFormatMetadata(GifFormat.Instance); + this.gifMetadata.ColorTableMode = this.logicalScreenDescriptor.GlobalColorTableFlag ? GifColorTableMode.Global : GifColorTableMode.Local; if (this.logicalScreenDescriptor.GlobalColorTableFlag) { int globalColorTableLength = this.logicalScreenDescriptor.GlobalColorTableSize * 3; - this.gifMetaData.GlobalColorTableLength = globalColorTableLength; + this.gifMetadata.GlobalColorTableLength = globalColorTableLength; this.globalColorTable = this.MemoryAllocator.AllocateManagedByteBuffer(globalColorTableLength, AllocationOptions.Clean); diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs index c8580144ad..e5a5463ebb 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs @@ -10,7 +10,7 @@ using System.Text; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Quantization; using SixLabors.Memory; @@ -58,9 +58,9 @@ namespace SixLabors.ImageSharp.Formats.Gif private int bitDepth; /// - /// Gif specific meta data. + /// Gif specific metadata. /// - private GifMetaData gifMetaData; + private GifMetadata gifMetadata; /// /// Initializes a new instance of the class. @@ -89,9 +89,9 @@ namespace SixLabors.ImageSharp.Formats.Gif this.configuration = image.GetConfiguration(); - ImageMetaData metaData = image.MetaData; - this.gifMetaData = metaData.GetFormatMetaData(GifFormat.Instance); - this.colorTableMode = this.colorTableMode ?? this.gifMetaData.ColorTableMode; + ImageMetadata metadata = image.Metadata; + this.gifMetadata = metadata.GetFormatMetadata(GifFormat.Instance); + this.colorTableMode = this.colorTableMode ?? this.gifMetadata.ColorTableMode; bool useGlobalTable = this.colorTableMode == GifColorTableMode.Global; // Quantize the image returning a palette. @@ -106,7 +106,7 @@ namespace SixLabors.ImageSharp.Formats.Gif // Write the LSD. int index = this.GetTransparentIndex(quantized); - this.WriteLogicalScreenDescriptor(metaData, image.Width, image.Height, index, useGlobalTable, stream); + this.WriteLogicalScreenDescriptor(metadata, image.Width, image.Height, index, useGlobalTable, stream); if (useGlobalTable) { @@ -114,12 +114,12 @@ namespace SixLabors.ImageSharp.Formats.Gif } // Write the comments. - this.WriteComments(metaData, stream); + this.WriteComments(metadata, stream); // Write application extension to allow additional frames. if (image.Frames.Count > 1) { - this.WriteApplicationExtension(stream, this.gifMetaData.RepeatCount); + this.WriteApplicationExtension(stream, this.gifMetadata.RepeatCount); } if (useGlobalTable) @@ -147,9 +147,9 @@ namespace SixLabors.ImageSharp.Formats.Gif for (int i = 0; i < image.Frames.Count; i++) { ImageFrame frame = image.Frames[i]; - ImageFrameMetaData metaData = frame.MetaData; - GifFrameMetaData frameMetaData = metaData.GetFormatMetaData(GifFormat.Instance); - this.WriteGraphicalControlExtension(frameMetaData, transparencyIndex, stream); + ImageFrameMetadata metadata = frame.Metadata; + GifFrameMetadata frameMetadata = metadata.GetFormatMetadata(GifFormat.Instance); + this.WriteGraphicalControlExtension(frameMetadata, transparencyIndex, stream); this.WriteImageDescriptor(frame, false, stream); if (i == 0) @@ -170,20 +170,20 @@ namespace SixLabors.ImageSharp.Formats.Gif where TPixel : struct, IPixel { ImageFrame previousFrame = null; - GifFrameMetaData previousMeta = null; + GifFrameMetadata previousMeta = null; foreach (ImageFrame frame in image.Frames) { - ImageFrameMetaData metaData = frame.MetaData; - GifFrameMetaData frameMetaData = metaData.GetFormatMetaData(GifFormat.Instance); + ImageFrameMetadata metadata = frame.Metadata; + GifFrameMetadata frameMetadata = metadata.GetFormatMetadata(GifFormat.Instance); if (quantized is null) { // Allow each frame to be encoded at whatever color depth the frame designates if set. - if (previousFrame != null && previousMeta.ColorTableLength != frameMetaData.ColorTableLength - && frameMetaData.ColorTableLength > 0) + if (previousFrame != null && previousMeta.ColorTableLength != frameMetadata.ColorTableLength + && frameMetadata.ColorTableLength > 0) { quantized = this.quantizer.CreateFrameQuantizer( image.GetConfiguration(), - frameMetaData.ColorTableLength).QuantizeFrame(frame); + frameMetadata.ColorTableLength).QuantizeFrame(frame); } else { @@ -193,7 +193,7 @@ namespace SixLabors.ImageSharp.Formats.Gif } this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(quantized.Palette.Length).Clamp(1, 8); - this.WriteGraphicalControlExtension(frameMetaData, this.GetTransparentIndex(quantized), stream); + this.WriteGraphicalControlExtension(frameMetadata, this.GetTransparentIndex(quantized), stream); this.WriteImageDescriptor(frame, true, stream); this.WriteColorTable(quantized, stream); this.WriteImageData(quantized, stream); @@ -201,7 +201,7 @@ namespace SixLabors.ImageSharp.Formats.Gif quantized?.Dispose(); quantized = null; // So next frame can regenerate it previousFrame = frame; - previousMeta = frameMetaData; + previousMeta = frameMetadata; } } @@ -250,14 +250,14 @@ namespace SixLabors.ImageSharp.Formats.Gif /// /// Writes the logical screen descriptor to the stream. /// - /// The image metadata. + /// The image metadata. /// The image width. /// The image height. /// The transparency index to set the default background index to. /// Whether to use a global or local color table. /// The stream to write to. private void WriteLogicalScreenDescriptor( - ImageMetaData metaData, + ImageMetadata metadata, int width, int height, int transparencyIndex, @@ -277,10 +277,10 @@ namespace SixLabors.ImageSharp.Formats.Gif // Aspect Ratio = (Pixel Aspect Ratio + 15) / 64 byte ratio = 0; - if (metaData.ResolutionUnits == PixelResolutionUnit.AspectRatio) + if (metadata.ResolutionUnits == PixelResolutionUnit.AspectRatio) { - double hr = metaData.HorizontalResolution; - double vr = metaData.VerticalResolution; + double hr = metadata.HorizontalResolution; + double vr = metadata.VerticalResolution; if (hr != vr) { if (hr > vr) @@ -326,7 +326,7 @@ namespace SixLabors.ImageSharp.Formats.Gif /// /// The metadata to be extract the comment data. /// The stream to write to. - private void WriteComments(ImageMetaData metadata, Stream stream) + private void WriteComments(ImageMetadata metadata, Stream stream) { if (!metadata.TryGetProperty(GifConstants.Comments, out ImageProperty property) || string.IsNullOrEmpty(property.Value)) @@ -350,18 +350,18 @@ namespace SixLabors.ImageSharp.Formats.Gif /// /// Writes the graphics control extension to the stream. /// - /// The metadata of the image or frame. + /// The metadata of the image or frame. /// The index of the color in the color palette to make transparent. /// The stream to write to. - private void WriteGraphicalControlExtension(GifFrameMetaData metaData, int transparencyIndex, Stream stream) + private void WriteGraphicalControlExtension(GifFrameMetadata metadata, int transparencyIndex, Stream stream) { byte packedValue = GifGraphicControlExtension.GetPackedValue( - disposalMethod: metaData.DisposalMethod, + disposalMethod: metadata.DisposalMethod, transparencyFlag: transparencyIndex > -1); var extension = new GifGraphicControlExtension( packed: packedValue, - delayTime: (ushort)metaData.FrameDelay, + delayTime: (ushort)metadata.FrameDelay, transparencyIndex: unchecked((byte)transparencyIndex)); this.WriteExtension(extension, stream); diff --git a/src/ImageSharp/Formats/Gif/GifFormat.cs b/src/ImageSharp/Formats/Gif/GifFormat.cs index 07d4a54547..abe87819c6 100644 --- a/src/ImageSharp/Formats/Gif/GifFormat.cs +++ b/src/ImageSharp/Formats/Gif/GifFormat.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Formats.Gif /// /// Registers the image encoders, decoders and mime type detectors for the gif format. /// - public sealed class GifFormat : IImageFormat + public sealed class GifFormat : IImageFormat { private GifFormat() { @@ -32,9 +32,9 @@ namespace SixLabors.ImageSharp.Formats.Gif public IEnumerable FileExtensions => GifConstants.FileExtensions; /// - public GifMetaData CreateDefaultFormatMetaData() => new GifMetaData(); + public GifMetadata CreateDefaultFormatMetadata() => new GifMetadata(); /// - public GifFrameMetaData CreateDefaultFormatFrameMetaData() => new GifFrameMetaData(); + public GifFrameMetadata CreateDefaultFormatFrameMetadata() => new GifFrameMetadata(); } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Gif/GifFrameMetaData.cs b/src/ImageSharp/Formats/Gif/GifFrameMetaData.cs index 0042c6a108..613825ad63 100644 --- a/src/ImageSharp/Formats/Gif/GifFrameMetaData.cs +++ b/src/ImageSharp/Formats/Gif/GifFrameMetaData.cs @@ -6,20 +6,20 @@ namespace SixLabors.ImageSharp.Formats.Gif /// /// Provides Gif specific metadata information for the image frame. /// - public class GifFrameMetaData : IDeepCloneable + public class GifFrameMetadata : IDeepCloneable { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public GifFrameMetaData() + public GifFrameMetadata() { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The metadata to create an instance from. - private GifFrameMetaData(GifFrameMetaData other) + private GifFrameMetadata(GifFrameMetadata other) { this.ColorTableLength = other.ColorTableLength; this.FrameDelay = other.FrameDelay; @@ -49,6 +49,6 @@ namespace SixLabors.ImageSharp.Formats.Gif public GifDisposalMethod DisposalMethod { get; set; } /// - public IDeepCloneable DeepClone() => new GifFrameMetaData(this); + public IDeepCloneable DeepClone() => new GifFrameMetadata(this); } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Gif/GifMetaData.cs b/src/ImageSharp/Formats/Gif/GifMetaData.cs index bb7fb50518..0b6566fbfe 100644 --- a/src/ImageSharp/Formats/Gif/GifMetaData.cs +++ b/src/ImageSharp/Formats/Gif/GifMetaData.cs @@ -6,20 +6,20 @@ namespace SixLabors.ImageSharp.Formats.Gif /// /// Provides Gif specific metadata information for the image. /// - public class GifMetaData : IDeepCloneable + public class GifMetadata : IDeepCloneable { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public GifMetaData() + public GifMetadata() { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The metadata to create an instance from. - private GifMetaData(GifMetaData other) + private GifMetadata(GifMetadata other) { this.RepeatCount = other.RepeatCount; this.ColorTableMode = other.ColorTableMode; @@ -45,6 +45,6 @@ namespace SixLabors.ImageSharp.Formats.Gif public int GlobalColorTableLength { get; set; } /// - public IDeepCloneable DeepClone() => new GifMetaData(this); + public IDeepCloneable DeepClone() => new GifMetadata(this); } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs b/src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs index 0c59163cce..871b511a0d 100644 --- a/src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs +++ b/src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System.Text; -using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.Metadata; namespace SixLabors.ImageSharp.Formats.Gif { diff --git a/src/ImageSharp/Formats/IImageFormat.cs b/src/ImageSharp/Formats/IImageFormat.cs index 3cd289df76..bd0d6357cb 100644 --- a/src/ImageSharp/Formats/IImageFormat.cs +++ b/src/ImageSharp/Formats/IImageFormat.cs @@ -34,30 +34,30 @@ namespace SixLabors.ImageSharp.Formats /// /// Defines the contract for an image format containing metadata. /// - /// The type of format metadata. - public interface IImageFormat : IImageFormat - where TFormatMetaData : class + /// The type of format metadata. + public interface IImageFormat : IImageFormat + where TFormatMetadata : class { /// /// Creates a default instance of the format metadata. /// - /// The . - TFormatMetaData CreateDefaultFormatMetaData(); + /// The . + TFormatMetadata CreateDefaultFormatMetadata(); } /// /// Defines the contract for an image format containing metadata with multiple frames. /// - /// The type of format metadata. - /// The type of format frame metadata. - public interface IImageFormat : IImageFormat - where TFormatMetaData : class - where TFormatFrameMetaData : class + /// The type of format metadata. + /// The type of format frame metadata. + public interface IImageFormat : IImageFormat + where TFormatMetadata : class + where TFormatFrameMetadata : class { /// /// Creates a default instance of the format frame metadata. /// - /// The . - TFormatFrameMetaData CreateDefaultFormatFrameMetaData(); + /// The . + TFormatFrameMetadata CreateDefaultFormatFrameMetadata(); } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs index 5bd5920dbe..7497c8a409 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; -using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.Metadata; namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 891dd52826..9852b36e49 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -12,9 +12,9 @@ using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.MetaData; -using SixLabors.ImageSharp.MetaData.Profiles.Exif; -using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using SixLabors.ImageSharp.Metadata; +using SixLabors.ImageSharp.Metadata.Profiles.Exif; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; using SixLabors.Memory; @@ -149,9 +149,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg public bool IgnoreMetadata { get; } /// - /// Gets the decoded by this decoder instance. + /// Gets the decoded by this decoder instance. /// - public ImageMetaData MetaData { get; private set; } + public ImageMetadata Metadata { get; private set; } /// public int ComponentCount { get; private set; } @@ -222,7 +222,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg this.ParseStream(stream); this.InitExifProfile(); this.InitIccProfile(); - this.InitDerivedMetaDataProperties(); + this.InitDerivedMetadataProperties(); return this.PostProcessIntoImage(); } @@ -235,9 +235,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg this.ParseStream(stream, true); this.InitExifProfile(); this.InitIccProfile(); - this.InitDerivedMetaDataProperties(); + this.InitDerivedMetadataProperties(); - return new ImageInfo(new PixelTypeInfo(this.BitsPerPixel), this.ImageWidth, this.ImageHeight, this.MetaData); + return new ImageInfo(new PixelTypeInfo(this.BitsPerPixel), this.ImageWidth, this.ImageHeight, this.Metadata); } /// @@ -247,7 +247,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// Whether to decode metadata only. public void ParseStream(Stream stream, bool metadataOnly = false) { - this.MetaData = new ImageMetaData(); + this.Metadata = new ImageMetadata(); this.InputStream = new DoubleBufferedStreamReader(this.configuration.MemoryAllocator, stream); // Check for the Start Of Image marker. @@ -430,7 +430,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg { if (this.isExif) { - this.MetaData.ExifProfile = new ExifProfile(this.exifData); + this.Metadata.ExifProfile = new ExifProfile(this.exifData); } } @@ -444,21 +444,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg var profile = new IccProfile(this.iccData); if (profile.CheckIsValid()) { - this.MetaData.IccProfile = profile; + this.Metadata.IccProfile = profile; } } } /// - /// Assigns derived metadata properties to , eg. horizontal and vertical resolution if it has a JFIF header. + /// Assigns derived metadata properties to , eg. horizontal and vertical resolution if it has a JFIF header. /// - private void InitDerivedMetaDataProperties() + private void InitDerivedMetadataProperties() { if (this.jFif.XDensity > 0 && this.jFif.YDensity > 0) { - this.MetaData.HorizontalResolution = this.jFif.XDensity; - this.MetaData.VerticalResolution = this.jFif.YDensity; - this.MetaData.ResolutionUnits = this.jFif.DensityUnits; + this.Metadata.HorizontalResolution = this.jFif.XDensity; + this.Metadata.VerticalResolution = this.jFif.YDensity; + this.Metadata.ResolutionUnits = this.jFif.DensityUnits; } else if (this.isExif) { @@ -467,16 +467,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg if (horizontalValue > 0 && verticalValue > 0) { - this.MetaData.HorizontalResolution = horizontalValue; - this.MetaData.VerticalResolution = verticalValue; - this.MetaData.ResolutionUnits = UnitConverter.ExifProfileToResolutionUnit(this.MetaData.ExifProfile); + this.Metadata.HorizontalResolution = horizontalValue; + this.Metadata.VerticalResolution = verticalValue; + this.Metadata.ResolutionUnits = UnitConverter.ExifProfileToResolutionUnit(this.Metadata.ExifProfile); } } } private double GetExifResolutionValue(ExifTag tag) { - if (!this.MetaData.ExifProfile.TryGetValue(tag, out ExifValue exifValue)) + if (!this.Metadata.ExifProfile.TryGetValue(tag, out ExifValue exifValue)) { return 0; } @@ -713,7 +713,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg JpegThrowHelper.ThrowBadMarker(nameof(JpegConstants.Markers.DQT), remaining); } - this.MetaData.GetFormatMetaData(JpegFormat.Instance).Quality = QualityEvaluator.EstimateQuality(this.QuantizationTables); + this.Metadata.GetFormatMetadata(JpegFormat.Instance).Quality = QualityEvaluator.EstimateQuality(this.QuantizationTables); } /// @@ -992,7 +992,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg this.configuration, this.ImageWidth, this.ImageHeight, - this.MetaData); + this.Metadata); using (var postProcessor = new JpegImagePostProcessor(this.configuration, this)) { diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs index 4544244f08..d4ce28107f 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs @@ -10,9 +10,9 @@ using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; using SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder; -using SixLabors.ImageSharp.MetaData; -using SixLabors.ImageSharp.MetaData.Profiles.Exif; -using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using SixLabors.ImageSharp.Metadata; +using SixLabors.ImageSharp.Metadata.Profiles.Exif; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Jpeg @@ -204,10 +204,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg } this.outputStream = stream; - ImageMetaData metaData = image.MetaData; + ImageMetadata metadata = image.Metadata; // System.Drawing produces identical output for jpegs with a quality parameter of 0 and 1. - int qlty = (this.quality ?? metaData.GetFormatMetaData(JpegFormat.Instance).Quality).Clamp(1, 100); + int qlty = (this.quality ?? metadata.GetFormatMetadata(JpegFormat.Instance).Quality).Clamp(1, 100); this.subsample = this.subsample ?? (qlty >= 91 ? JpegSubsample.Ratio444 : JpegSubsample.Ratio420); // Convert from a quality rating to a scaling factor. @@ -229,10 +229,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg int componentCount = 3; // Write the Start Of Image marker. - this.WriteApplicationHeader(metaData); + this.WriteApplicationHeader(metadata); // Write Exif and ICC profiles - this.WriteProfiles(metaData); + this.WriteProfiles(metadata); // Write the quantization tables. this.WriteDefineQuantizationTables(); @@ -448,8 +448,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// /// Writes the application header containing the JFIF identifier plus extra data. /// - /// The image meta data. - private void WriteApplicationHeader(ImageMetaData meta) + /// The image metadata. + private void WriteApplicationHeader(ImageMetadata meta) { // Write the start of image marker. Markers are always prefixed with 0xff. this.buffer[0] = JpegConstants.Markers.XFF; @@ -791,17 +791,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// /// Writes the metadata profiles to the image. /// - /// The image meta data. - private void WriteProfiles(ImageMetaData metaData) + /// The image metadata. + private void WriteProfiles(ImageMetadata metadata) { - if (metaData is null) + if (metadata is null) { return; } - metaData.SyncProfiles(); - this.WriteExifProfile(metaData.ExifProfile); - this.WriteIccProfile(metaData.IccProfile); + metadata.SyncProfiles(); + this.WriteExifProfile(metadata.ExifProfile); + this.WriteIccProfile(metadata.IccProfile); } /// diff --git a/src/ImageSharp/Formats/Jpeg/JpegFormat.cs b/src/ImageSharp/Formats/Jpeg/JpegFormat.cs index 6b23ceac7a..f56072a4b5 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegFormat.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegFormat.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// /// Registers the image encoders, decoders and mime type detectors for the jpeg format. /// - public sealed class JpegFormat : IImageFormat + public sealed class JpegFormat : IImageFormat { private JpegFormat() { @@ -32,6 +32,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg public IEnumerable FileExtensions => JpegConstants.FileExtensions; /// - public JpegMetaData CreateDefaultFormatMetaData() => new JpegMetaData(); + public JpegMetadata CreateDefaultFormatMetadata() => new JpegMetadata(); } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/JpegMetaData.cs b/src/ImageSharp/Formats/Jpeg/JpegMetaData.cs index fcad29e5d0..9f0deae02c 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegMetaData.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegMetaData.cs @@ -6,20 +6,20 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// /// Provides Jpeg specific metadata information for the image. /// - public class JpegMetaData : IDeepCloneable + public class JpegMetadata : IDeepCloneable { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public JpegMetaData() + public JpegMetadata() { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The metadata to create an instance from. - private JpegMetaData(JpegMetaData other) => this.Quality = other.Quality; + private JpegMetadata(JpegMetadata other) => this.Quality = other.Quality; /// /// Gets or sets the encoded quality. @@ -27,6 +27,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg public int Quality { get; set; } = 75; /// - public IDeepCloneable DeepClone() => new JpegMetaData(this); + public IDeepCloneable DeepClone() => new JpegMetadata(this); } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Png/Chunks/PhysicalChunkData.cs b/src/ImageSharp/Formats/Png/Chunks/PhysicalChunkData.cs index 1d0e280bd3..8b3c3e9aad 100644 --- a/src/ImageSharp/Formats/Png/Chunks/PhysicalChunkData.cs +++ b/src/ImageSharp/Formats/Png/Chunks/PhysicalChunkData.cs @@ -4,7 +4,7 @@ using System; using System.Buffers.Binary; using SixLabors.ImageSharp.Common.Helpers; -using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.Metadata; namespace SixLabors.ImageSharp.Formats.Png.Chunks { @@ -60,7 +60,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Chunks /// /// The metadata. /// The constructed PngPhysicalChunkData instance. - public static PhysicalChunkData FromMetadata(ImageMetaData meta) + public static PhysicalChunkData FromMetadata(ImageMetadata meta) { byte unitSpecifier = 0; uint x; diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 9ffac5e626..5e9d1440ac 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -12,8 +12,8 @@ using SixLabors.ImageSharp.Formats.Png.Chunks; using SixLabors.ImageSharp.Formats.Png.Filters; using SixLabors.ImageSharp.Formats.Png.Zlib; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.MetaData; -using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using SixLabors.ImageSharp.Metadata; +using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Memory; @@ -157,8 +157,8 @@ namespace SixLabors.ImageSharp.Formats.Png public Image Decode(Stream stream) where TPixel : struct, IPixel { - var metaData = new ImageMetaData(); - PngMetaData pngMetaData = metaData.GetFormatMetaData(PngFormat.Instance); + var metadata = new ImageMetadata(); + PngMetadata pngMetadata = metadata.GetFormatMetadata(PngFormat.Instance); this.currentStream = stream; this.currentStream.Skip(8); Image image = null; @@ -171,24 +171,24 @@ namespace SixLabors.ImageSharp.Formats.Png switch (chunk.Type) { case PngChunkType.Header: - this.ReadHeaderChunk(pngMetaData, chunk.Data.Array); + this.ReadHeaderChunk(pngMetadata, chunk.Data.Array); break; case PngChunkType.Physical: - this.ReadPhysicalChunk(metaData, chunk.Data.GetSpan()); + this.ReadPhysicalChunk(metadata, chunk.Data.GetSpan()); break; case PngChunkType.Gamma: - this.ReadGammaChunk(pngMetaData, chunk.Data.GetSpan()); + this.ReadGammaChunk(pngMetadata, chunk.Data.GetSpan()); break; case PngChunkType.Data: if (image is null) { - this.InitializeImage(metaData, out image); + this.InitializeImage(metadata, out image); } using (var deframeStream = new ZlibInflateStream(this.currentStream, this.ReadNextDataChunk)) { deframeStream.AllocateNewBytes(chunk.Length); - this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame, pngMetaData); + this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame, pngMetadata); } break; @@ -201,17 +201,17 @@ namespace SixLabors.ImageSharp.Formats.Png byte[] alpha = new byte[chunk.Length]; Buffer.BlockCopy(chunk.Data.Array, 0, alpha, 0, chunk.Length); this.paletteAlpha = alpha; - this.AssignTransparentMarkers(alpha, pngMetaData); + this.AssignTransparentMarkers(alpha, pngMetadata); break; case PngChunkType.Text: - this.ReadTextChunk(metaData, chunk.Data.Array.AsSpan(0, chunk.Length)); + this.ReadTextChunk(metadata, chunk.Data.Array.AsSpan(0, chunk.Length)); break; case PngChunkType.Exif: if (!this.ignoreMetadata) { byte[] exifData = new byte[chunk.Length]; Buffer.BlockCopy(chunk.Data.Array, 0, exifData, 0, chunk.Length); - metaData.ExifProfile = new ExifProfile(exifData); + metadata.ExifProfile = new ExifProfile(exifData); } break; @@ -246,8 +246,8 @@ namespace SixLabors.ImageSharp.Formats.Png /// The containing image data. public IImageInfo Identify(Stream stream) { - var metaData = new ImageMetaData(); - PngMetaData pngMetaData = metaData.GetFormatMetaData(PngFormat.Instance); + var metadata = new ImageMetadata(); + PngMetadata pngMetadata = metadata.GetFormatMetadata(PngFormat.Instance); this.currentStream = stream; this.currentStream.Skip(8); try @@ -259,19 +259,19 @@ namespace SixLabors.ImageSharp.Formats.Png switch (chunk.Type) { case PngChunkType.Header: - this.ReadHeaderChunk(pngMetaData, chunk.Data.Array); + this.ReadHeaderChunk(pngMetadata, chunk.Data.Array); break; case PngChunkType.Physical: - this.ReadPhysicalChunk(metaData, chunk.Data.GetSpan()); + this.ReadPhysicalChunk(metadata, chunk.Data.GetSpan()); break; case PngChunkType.Gamma: - this.ReadGammaChunk(pngMetaData, chunk.Data.GetSpan()); + this.ReadGammaChunk(pngMetadata, chunk.Data.GetSpan()); break; case PngChunkType.Data: this.SkipChunkDataAndCrc(chunk); break; case PngChunkType.Text: - this.ReadTextChunk(metaData, chunk.Data.Array.AsSpan(0, chunk.Length)); + this.ReadTextChunk(metadata, chunk.Data.Array.AsSpan(0, chunk.Length)); break; case PngChunkType.End: this.isEndChunkReached = true; @@ -295,7 +295,7 @@ namespace SixLabors.ImageSharp.Formats.Png throw new ImageFormatException("PNG Image does not contain a header chunk"); } - return new ImageInfo(new PixelTypeInfo(this.CalculateBitsPerPixel()), this.header.Width, this.header.Height, metaData); + return new ImageInfo(new PixelTypeInfo(this.CalculateBitsPerPixel()), this.header.Width, this.header.Height, metadata); } /// @@ -350,7 +350,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// The metadata to read to. /// The data containing physical data. - private void ReadPhysicalChunk(ImageMetaData metadata, ReadOnlySpan data) + private void ReadPhysicalChunk(ImageMetadata metadata, ReadOnlySpan data) { var physicalChunk = PhysicalChunkData.Parse(data); @@ -367,7 +367,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// The metadata to read to. /// The data containing physical data. - private void ReadGammaChunk(PngMetaData pngMetadata, ReadOnlySpan data) + private void ReadGammaChunk(PngMetadata pngMetadata, ReadOnlySpan data) { // The value is encoded as a 4-byte unsigned integer, representing gamma times 100000. // For example, a gamma of 1/2.2 would be stored as 45455. @@ -380,7 +380,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// The type the pixels will be /// The metadata information for the image /// The image that we will populate - private void InitializeImage(ImageMetaData metadata, out Image image) + private void InitializeImage(ImageMetadata metadata, out Image image) where TPixel : struct, IPixel { image = new Image(this.configuration, this.header.Width, this.header.Height, metadata); @@ -471,17 +471,17 @@ namespace SixLabors.ImageSharp.Formats.Png /// The pixel format. /// The containing data. /// The pixel data. - /// The png meta data - private void ReadScanlines(Stream dataStream, ImageFrame image, PngMetaData pngMetaData) + /// The png metadata + private void ReadScanlines(Stream dataStream, ImageFrame image, PngMetadata pngMetadata) where TPixel : struct, IPixel { if (this.header.InterlaceMethod == PngInterlaceMode.Adam7) { - this.DecodeInterlacedPixelData(dataStream, image, pngMetaData); + this.DecodeInterlacedPixelData(dataStream, image, pngMetadata); } else { - this.DecodePixelData(dataStream, image, pngMetaData); + this.DecodePixelData(dataStream, image, pngMetadata); } } @@ -491,8 +491,8 @@ namespace SixLabors.ImageSharp.Formats.Png /// The pixel format. /// The compressed pixel data stream. /// The image to decode to. - /// The png meta data - private void DecodePixelData(Stream compressedStream, ImageFrame image, PngMetaData pngMetaData) + /// The png metadata + private void DecodePixelData(Stream compressedStream, ImageFrame image, PngMetadata pngMetadata) where TPixel : struct, IPixel { while (this.currentRow < this.header.Height) @@ -532,7 +532,7 @@ namespace SixLabors.ImageSharp.Formats.Png throw new ImageFormatException("Unknown filter type."); } - this.ProcessDefilteredScanline(scanlineSpan, image, pngMetaData); + this.ProcessDefilteredScanline(scanlineSpan, image, pngMetadata); this.SwapBuffers(); this.currentRow++; @@ -546,8 +546,8 @@ namespace SixLabors.ImageSharp.Formats.Png /// The pixel format. /// The compressed pixel data stream. /// The current image. - /// The png meta data - private void DecodeInterlacedPixelData(Stream compressedStream, ImageFrame image, PngMetaData pngMetaData) + /// The png metadata. + private void DecodeInterlacedPixelData(Stream compressedStream, ImageFrame image, PngMetadata pngMetadata) where TPixel : struct, IPixel { while (true) @@ -604,7 +604,7 @@ namespace SixLabors.ImageSharp.Formats.Png } Span rowSpan = image.GetPixelRowSpan(this.currentRow); - this.ProcessInterlacedDefilteredScanline(this.scanline.GetSpan(), rowSpan, pngMetaData, Adam7.FirstColumn[this.pass], Adam7.ColumnIncrement[this.pass]); + this.ProcessInterlacedDefilteredScanline(this.scanline.GetSpan(), rowSpan, pngMetadata, Adam7.FirstColumn[this.pass], Adam7.ColumnIncrement[this.pass]); this.SwapBuffers(); @@ -632,8 +632,8 @@ namespace SixLabors.ImageSharp.Formats.Png /// The pixel format. /// The de-filtered scanline /// The image - /// The png meta data - private void ProcessDefilteredScanline(ReadOnlySpan defilteredScanline, ImageFrame pixels, PngMetaData pngMetaData) + /// The png metadata. + private void ProcessDefilteredScanline(ReadOnlySpan defilteredScanline, ImageFrame pixels, PngMetadata pngMetadata) where TPixel : struct, IPixel { Span rowSpan = pixels.GetPixelRowSpan(this.currentRow); @@ -653,9 +653,9 @@ namespace SixLabors.ImageSharp.Formats.Png this.header, scanlineSpan, rowSpan, - pngMetaData.HasTrans, - pngMetaData.TransparentGray16.GetValueOrDefault(), - pngMetaData.TransparentGray8.GetValueOrDefault()); + pngMetadata.HasTrans, + pngMetadata.TransparentGray16.GetValueOrDefault(), + pngMetadata.TransparentGray8.GetValueOrDefault()); break; @@ -687,9 +687,9 @@ namespace SixLabors.ImageSharp.Formats.Png rowSpan, this.bytesPerPixel, this.bytesPerSample, - pngMetaData.HasTrans, - pngMetaData.TransparentRgb48.GetValueOrDefault(), - pngMetaData.TransparentRgb24.GetValueOrDefault()); + pngMetadata.HasTrans, + pngMetadata.TransparentRgb48.GetValueOrDefault(), + pngMetadata.TransparentRgb24.GetValueOrDefault()); break; @@ -714,10 +714,10 @@ namespace SixLabors.ImageSharp.Formats.Png /// The pixel format. /// The de-filtered scanline /// The current image row. - /// The png meta data + /// The png metadata. /// The column start index. Always 0 for none interlaced images. /// The column increment. Always 1 for none interlaced images. - private void ProcessInterlacedDefilteredScanline(ReadOnlySpan defilteredScanline, Span rowSpan, PngMetaData pngMetaData, int pixelOffset = 0, int increment = 1) + private void ProcessInterlacedDefilteredScanline(ReadOnlySpan defilteredScanline, Span rowSpan, PngMetadata pngMetadata, int pixelOffset = 0, int increment = 1) where TPixel : struct, IPixel { // Trim the first marker byte from the buffer @@ -737,9 +737,9 @@ namespace SixLabors.ImageSharp.Formats.Png rowSpan, pixelOffset, increment, - pngMetaData.HasTrans, - pngMetaData.TransparentGray16.GetValueOrDefault(), - pngMetaData.TransparentGray8.GetValueOrDefault()); + pngMetadata.HasTrans, + pngMetadata.TransparentGray16.GetValueOrDefault(), + pngMetadata.TransparentGray8.GetValueOrDefault()); break; @@ -776,9 +776,9 @@ namespace SixLabors.ImageSharp.Formats.Png increment, this.bytesPerPixel, this.bytesPerSample, - pngMetaData.HasTrans, - pngMetaData.TransparentRgb48.GetValueOrDefault(), - pngMetaData.TransparentRgb24.GetValueOrDefault()); + pngMetadata.HasTrans, + pngMetadata.TransparentRgb48.GetValueOrDefault(), + pngMetadata.TransparentRgb24.GetValueOrDefault()); break; @@ -799,11 +799,11 @@ namespace SixLabors.ImageSharp.Formats.Png } /// - /// Decodes and assigns marker colors that identify transparent pixels in non indexed images + /// Decodes and assigns marker colors that identify transparent pixels in non indexed images. /// - /// The alpha tRNS array - /// The png meta data - private void AssignTransparentMarkers(ReadOnlySpan alpha, PngMetaData pngMetaData) + /// The alpha tRNS array. + /// The png metadata. + private void AssignTransparentMarkers(ReadOnlySpan alpha, PngMetadata pngMetadata) { if (this.pngColorType == PngColorType.Rgb) { @@ -815,16 +815,16 @@ namespace SixLabors.ImageSharp.Formats.Png ushort gc = BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(2, 2)); ushort bc = BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(4, 2)); - pngMetaData.TransparentRgb48 = new Rgb48(rc, gc, bc); - pngMetaData.HasTrans = true; + pngMetadata.TransparentRgb48 = new Rgb48(rc, gc, bc); + pngMetadata.HasTrans = true; return; } byte r = ReadByteLittleEndian(alpha, 0); byte g = ReadByteLittleEndian(alpha, 2); byte b = ReadByteLittleEndian(alpha, 4); - pngMetaData.TransparentRgb24 = new Rgb24(r, g, b); - pngMetaData.HasTrans = true; + pngMetadata.TransparentRgb24 = new Rgb24(r, g, b); + pngMetadata.HasTrans = true; } } else if (this.pngColorType == PngColorType.Grayscale) @@ -833,14 +833,14 @@ namespace SixLabors.ImageSharp.Formats.Png { if (this.header.BitDepth == 16) { - pngMetaData.TransparentGray16 = new Gray16(BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(0, 2))); + pngMetadata.TransparentGray16 = new Gray16(BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(0, 2))); } else { - pngMetaData.TransparentGray8 = new Gray8(ReadByteLittleEndian(alpha, 0)); + pngMetadata.TransparentGray8 = new Gray8(ReadByteLittleEndian(alpha, 0)); } - pngMetaData.HasTrans = true; + pngMetadata.HasTrans = true; } } } @@ -848,16 +848,16 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// Reads a header chunk from the data. /// - /// The png metadata. + /// The png metadata. /// The containing data. - private void ReadHeaderChunk(PngMetaData pngMetaData, ReadOnlySpan data) + private void ReadHeaderChunk(PngMetadata pngMetadata, ReadOnlySpan data) { this.header = PngHeader.Parse(data); this.header.Validate(); - pngMetaData.BitDepth = (PngBitDepth)this.header.BitDepth; - pngMetaData.ColorType = this.header.ColorType; + pngMetadata.BitDepth = (PngBitDepth)this.header.BitDepth; + pngMetadata.ColorType = this.header.ColorType; this.pngColorType = this.header.ColorType; } @@ -867,7 +867,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// The metadata to decode to. /// The containing the data. - private void ReadTextChunk(ImageMetaData metadata, ReadOnlySpan data) + private void ReadTextChunk(ImageMetadata metadata, ReadOnlySpan data) { if (this.ignoreMetadata) { diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 96e2dd946d..9818e6cf17 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -14,7 +14,7 @@ using SixLabors.ImageSharp.Formats.Png.Chunks; using SixLabors.ImageSharp.Formats.Png.Filters; using SixLabors.ImageSharp.Formats.Png.Zlib; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Quantization; using SixLabors.Memory; @@ -211,12 +211,12 @@ namespace SixLabors.ImageSharp.Formats.Png this.height = image.Height; // Always take the encoder options over the metadata values. - ImageMetaData metaData = image.MetaData; - PngMetaData pngMetaData = metaData.GetFormatMetaData(PngFormat.Instance); - this.gamma = this.gamma ?? pngMetaData.Gamma; + ImageMetadata metadata = image.Metadata; + PngMetadata pngMetadata = metadata.GetFormatMetadata(PngFormat.Instance); + this.gamma = this.gamma ?? pngMetadata.Gamma; this.writeGamma = this.gamma > 0; - this.pngColorType = this.pngColorType ?? pngMetaData.ColorType; - this.pngBitDepth = this.pngBitDepth ?? pngMetaData.BitDepth; + this.pngColorType = this.pngColorType ?? pngMetadata.ColorType; + this.pngBitDepth = this.pngBitDepth ?? pngMetadata.BitDepth; this.use16Bit = this.pngBitDepth == PngBitDepth.Bit16; // Ensure we are not allowing impossible combinations. @@ -290,14 +290,14 @@ namespace SixLabors.ImageSharp.Formats.Png this.WritePaletteChunk(stream, quantized); } - if (pngMetaData.HasTrans) + if (pngMetadata.HasTrans) { - this.WriteTransparencyChunk(stream, pngMetaData); + this.WriteTransparencyChunk(stream, pngMetadata); } - this.WritePhysicalChunk(stream, metaData); + this.WritePhysicalChunk(stream, metadata); this.WriteGammaChunk(stream); - this.WriteExifChunk(stream, metaData); + this.WriteExifChunk(stream, metadata); this.WriteDataChunks(image.Frames.RootFrame, quantized, stream); this.WriteEndChunk(stream); stream.Flush(); @@ -714,8 +714,8 @@ namespace SixLabors.ImageSharp.Formats.Png /// Writes the physical dimension information to the stream. /// /// The containing image data. - /// The image meta data. - private void WritePhysicalChunk(Stream stream, ImageMetaData meta) + /// The image metadata. + private void WritePhysicalChunk(Stream stream, ImageMetadata meta) { PhysicalChunkData.FromMetadata(meta).WriteTo(this.chunkDataBuffer); @@ -723,11 +723,11 @@ namespace SixLabors.ImageSharp.Formats.Png } /// - /// Writes the eXIf chunk to the stream, if any EXIF Profile values are present in the meta data. + /// Writes the eXIf chunk to the stream, if any EXIF Profile values are present in the metadata. /// /// The containing image data. - /// The image meta data. - private void WriteExifChunk(Stream stream, ImageMetaData meta) + /// The image metadata. + private void WriteExifChunk(Stream stream, ImageMetadata meta) { if (meta.ExifProfile?.Values.Count > 0) { @@ -757,42 +757,42 @@ namespace SixLabors.ImageSharp.Formats.Png /// Writes the transparency chunk to the stream /// /// The containing image data. - /// The image meta data. - private void WriteTransparencyChunk(Stream stream, PngMetaData pngMetaData) + /// The image metadata. + private void WriteTransparencyChunk(Stream stream, PngMetadata pngMetadata) { Span alpha = this.chunkDataBuffer.AsSpan(); - if (pngMetaData.ColorType == PngColorType.Rgb) + if (pngMetadata.ColorType == PngColorType.Rgb) { - if (pngMetaData.TransparentRgb48.HasValue && this.use16Bit) + if (pngMetadata.TransparentRgb48.HasValue && this.use16Bit) { - Rgb48 rgb = pngMetaData.TransparentRgb48.Value; + Rgb48 rgb = pngMetadata.TransparentRgb48.Value; BinaryPrimitives.WriteUInt16LittleEndian(alpha, rgb.R); BinaryPrimitives.WriteUInt16LittleEndian(alpha.Slice(2, 2), rgb.G); BinaryPrimitives.WriteUInt16LittleEndian(alpha.Slice(4, 2), rgb.B); this.WriteChunk(stream, PngChunkType.Transparency, this.chunkDataBuffer, 0, 6); } - else if (pngMetaData.TransparentRgb24.HasValue) + else if (pngMetadata.TransparentRgb24.HasValue) { alpha.Clear(); - Rgb24 rgb = pngMetaData.TransparentRgb24.Value; + Rgb24 rgb = pngMetadata.TransparentRgb24.Value; alpha[1] = rgb.R; alpha[3] = rgb.G; alpha[5] = rgb.B; this.WriteChunk(stream, PngChunkType.Transparency, this.chunkDataBuffer, 0, 6); } } - else if (pngMetaData.ColorType == PngColorType.Grayscale) + else if (pngMetadata.ColorType == PngColorType.Grayscale) { - if (pngMetaData.TransparentGray16.HasValue && this.use16Bit) + if (pngMetadata.TransparentGray16.HasValue && this.use16Bit) { - BinaryPrimitives.WriteUInt16LittleEndian(alpha, pngMetaData.TransparentGray16.Value.PackedValue); + BinaryPrimitives.WriteUInt16LittleEndian(alpha, pngMetadata.TransparentGray16.Value.PackedValue); this.WriteChunk(stream, PngChunkType.Transparency, this.chunkDataBuffer, 0, 2); } - else if (pngMetaData.TransparentGray8.HasValue) + else if (pngMetadata.TransparentGray8.HasValue) { alpha.Clear(); - alpha[1] = pngMetaData.TransparentGray8.Value.PackedValue; + alpha[1] = pngMetadata.TransparentGray8.Value.PackedValue; this.WriteChunk(stream, PngChunkType.Transparency, this.chunkDataBuffer, 0, 2); } } diff --git a/src/ImageSharp/Formats/Png/PngFormat.cs b/src/ImageSharp/Formats/Png/PngFormat.cs index 210e2a837d..408e37802f 100644 --- a/src/ImageSharp/Formats/Png/PngFormat.cs +++ b/src/ImageSharp/Formats/Png/PngFormat.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// Registers the image encoders, decoders and mime type detectors for the png format. /// - public sealed class PngFormat : IImageFormat + public sealed class PngFormat : IImageFormat { private PngFormat() { @@ -32,6 +32,6 @@ namespace SixLabors.ImageSharp.Formats.Png public IEnumerable FileExtensions => PngConstants.FileExtensions; /// - public PngMetaData CreateDefaultFormatMetaData() => new PngMetaData(); + public PngMetadata CreateDefaultFormatMetadata() => new PngMetadata(); } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Png/PngMetaData.cs b/src/ImageSharp/Formats/Png/PngMetaData.cs index d5ab3d2554..dd951763f7 100644 --- a/src/ImageSharp/Formats/Png/PngMetaData.cs +++ b/src/ImageSharp/Formats/Png/PngMetaData.cs @@ -8,20 +8,20 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// Provides Png specific metadata information for the image. /// - public class PngMetaData : IDeepCloneable + public class PngMetadata : IDeepCloneable { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public PngMetaData() + public PngMetadata() { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The metadata to create an instance from. - private PngMetaData(PngMetaData other) + private PngMetadata(PngMetadata other) { this.BitDepth = other.BitDepth; this.ColorType = other.ColorType; @@ -75,6 +75,6 @@ namespace SixLabors.ImageSharp.Formats.Png public bool HasTrans { get; set; } /// - public IDeepCloneable DeepClone() => new PngMetaData(this); + public IDeepCloneable DeepClone() => new PngMetadata(this); } } \ No newline at end of file diff --git a/src/ImageSharp/IImageInfo.cs b/src/ImageSharp/IImageInfo.cs index b1b1afc66f..b270c2c4de 100644 --- a/src/ImageSharp/IImageInfo.cs +++ b/src/ImageSharp/IImageInfo.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Formats; -using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.Metadata; namespace SixLabors.ImageSharp { @@ -30,6 +30,6 @@ namespace SixLabors.ImageSharp /// /// Gets the metadata of the image. /// - ImageMetaData MetaData { get; } + ImageMetadata Metadata { get; } } } \ No newline at end of file diff --git a/src/ImageSharp/Image.Decode.cs b/src/ImageSharp/Image.Decode.cs index ffdab25e24..9e83d173f2 100644 --- a/src/ImageSharp/Image.Decode.cs +++ b/src/ImageSharp/Image.Decode.cs @@ -5,7 +5,7 @@ using System.IO; using System.Linq; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Memory; @@ -25,13 +25,13 @@ namespace SixLabors.ImageSharp /// The /// The width of the image /// The height of the image - /// The + /// The /// The result internal static Image CreateUninitialized( Configuration configuration, int width, int height, - ImageMetaData metadata) + ImageMetadata metadata) where TPixel : struct, IPixel { Buffer2D uninitializedMemoryBuffer = diff --git a/src/ImageSharp/Image.WrapMemory.cs b/src/ImageSharp/Image.WrapMemory.cs index 4bb24a126b..3d788bb73a 100644 --- a/src/ImageSharp/Image.WrapMemory.cs +++ b/src/ImageSharp/Image.WrapMemory.cs @@ -5,7 +5,7 @@ using System; using System.Buffers; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp @@ -24,18 +24,18 @@ namespace SixLabors.ImageSharp /// The pixel memory. /// The width of the memory image. /// The height of the memory image. - /// The . + /// The . /// An instance public static Image WrapMemory( Configuration config, Memory pixelMemory, int width, int height, - ImageMetaData metaData) + ImageMetadata metadata) where TPixel : struct, IPixel { var memorySource = new MemorySource(pixelMemory); - return new Image(config, memorySource, width, height, metaData); + return new Image(config, memorySource, width, height, metadata); } /// @@ -55,7 +55,7 @@ namespace SixLabors.ImageSharp int height) where TPixel : struct, IPixel { - return WrapMemory(config, pixelMemory, width, height, new ImageMetaData()); + return WrapMemory(config, pixelMemory, width, height, new ImageMetadata()); } /// @@ -89,18 +89,18 @@ namespace SixLabors.ImageSharp /// The that is being transferred to the image /// The width of the memory image. /// The height of the memory image. - /// The + /// The /// An instance public static Image WrapMemory( Configuration config, IMemoryOwner pixelMemoryOwner, int width, int height, - ImageMetaData metaData) + ImageMetadata metadata) where TPixel : struct, IPixel { var memorySource = new MemorySource(pixelMemoryOwner, false); - return new Image(config, memorySource, width, height, metaData); + return new Image(config, memorySource, width, height, metadata); } /// @@ -123,7 +123,7 @@ namespace SixLabors.ImageSharp int height) where TPixel : struct, IPixel { - return WrapMemory(config, pixelMemoryOwner, width, height, new ImageMetaData()); + return WrapMemory(config, pixelMemoryOwner, width, height, new ImageMetadata()); } /// diff --git a/src/ImageSharp/ImageFrameCollection.cs b/src/ImageSharp/ImageFrameCollection.cs index bbe05ce435..7e516434cb 100644 --- a/src/ImageSharp/ImageFrameCollection.cs +++ b/src/ImageSharp/ImageFrameCollection.cs @@ -192,7 +192,7 @@ namespace SixLabors.ImageSharp this.frames.Remove(frame); - return new Image(this.parent.GetConfiguration(), this.parent.MetaData.DeepClone(), new[] { frame }); + return new Image(this.parent.GetConfiguration(), this.parent.Metadata.DeepClone(), new[] { frame }); } /// @@ -205,7 +205,7 @@ namespace SixLabors.ImageSharp { ImageFrame frame = this[index]; ImageFrame clonedFrame = frame.Clone(); - return new Image(this.parent.GetConfiguration(), this.parent.MetaData.DeepClone(), new[] { clonedFrame }); + return new Image(this.parent.GetConfiguration(), this.parent.Metadata.DeepClone(), new[] { clonedFrame }); } /// diff --git a/src/ImageSharp/ImageFrame{TPixel}.cs b/src/ImageSharp/ImageFrame{TPixel}.cs index f69ae37574..4f0be59713 100644 --- a/src/ImageSharp/ImageFrame{TPixel}.cs +++ b/src/ImageSharp/ImageFrame{TPixel}.cs @@ -6,7 +6,7 @@ using System.Runtime.CompilerServices; using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Memory; @@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp /// The width of the image in pixels. /// The height of the image in pixels. internal ImageFrame(Configuration configuration, int width, int height) - : this(configuration, width, height, new ImageFrameMetaData()) + : this(configuration, width, height, new ImageFrameMetadata()) { } @@ -39,9 +39,9 @@ namespace SixLabors.ImageSharp /// /// The configuration which allows altering default behaviour or extending the library. /// The of the frame. - /// The meta data. - internal ImageFrame(Configuration configuration, Size size, ImageFrameMetaData metaData) - : this(configuration, size.Width, size.Height, metaData) + /// The metadata. + internal ImageFrame(Configuration configuration, Size size, ImageFrameMetadata metadata) + : this(configuration, size.Width, size.Height, metadata) { } @@ -51,9 +51,9 @@ namespace SixLabors.ImageSharp /// The configuration which allows altering default behaviour or extending the library. /// The width of the image in pixels. /// The height of the image in pixels. - /// The meta data. - internal ImageFrame(Configuration configuration, int width, int height, ImageFrameMetaData metaData) - : this(configuration, width, height, default(TPixel), metaData) + /// The metadata. + internal ImageFrame(Configuration configuration, int width, int height, ImageFrameMetadata metadata) + : this(configuration, width, height, default(TPixel), metadata) { } @@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp /// The height of the image in pixels. /// The color to clear the image with. internal ImageFrame(Configuration configuration, int width, int height, TPixel backgroundColor) - : this(configuration, width, height, backgroundColor, new ImageFrameMetaData()) + : this(configuration, width, height, backgroundColor, new ImageFrameMetadata()) { } @@ -76,8 +76,8 @@ namespace SixLabors.ImageSharp /// The width of the image in pixels. /// The height of the image in pixels. /// The color to clear the image with. - /// The meta data. - internal ImageFrame(Configuration configuration, int width, int height, TPixel backgroundColor, ImageFrameMetaData metaData) + /// The metadata. + internal ImageFrame(Configuration configuration, int width, int height, TPixel backgroundColor, ImageFrameMetadata metadata) { Guard.NotNull(configuration, nameof(configuration)); Guard.MustBeGreaterThan(width, 0, nameof(width)); @@ -86,7 +86,7 @@ namespace SixLabors.ImageSharp this.Configuration = configuration; this.MemoryAllocator = configuration.MemoryAllocator; this.PixelBuffer = this.MemoryAllocator.Allocate2D(width, height); - this.MetaData = metaData ?? new ImageFrameMetaData(); + this.Metadata = metadata ?? new ImageFrameMetadata(); this.Clear(configuration.GetParallelOptions(), backgroundColor); } @@ -98,7 +98,7 @@ namespace SixLabors.ImageSharp /// The height of the image in pixels. /// The memory source. internal ImageFrame(Configuration configuration, int width, int height, MemorySource memorySource) - : this(configuration, width, height, memorySource, new ImageFrameMetaData()) + : this(configuration, width, height, memorySource, new ImageFrameMetadata()) { } @@ -109,18 +109,18 @@ namespace SixLabors.ImageSharp /// The width of the image in pixels. /// The height of the image in pixels. /// The memory source. - /// The meta data. - internal ImageFrame(Configuration configuration, int width, int height, MemorySource memorySource, ImageFrameMetaData metaData) + /// The metadata. + internal ImageFrame(Configuration configuration, int width, int height, MemorySource memorySource, ImageFrameMetadata metadata) { Guard.NotNull(configuration, nameof(configuration)); Guard.MustBeGreaterThan(width, 0, nameof(width)); Guard.MustBeGreaterThan(height, 0, nameof(height)); - Guard.NotNull(metaData, nameof(metaData)); + Guard.NotNull(metadata, nameof(metadata)); this.Configuration = configuration; this.MemoryAllocator = configuration.MemoryAllocator; this.PixelBuffer = new Buffer2D(memorySource, width, height); - this.MetaData = metaData; + this.Metadata = metadata; } /// @@ -137,7 +137,7 @@ namespace SixLabors.ImageSharp this.MemoryAllocator = configuration.MemoryAllocator; this.PixelBuffer = this.MemoryAllocator.Allocate2D(source.PixelBuffer.Width, source.PixelBuffer.Height); source.PixelBuffer.GetSpan().CopyTo(this.PixelBuffer.GetSpan()); - this.MetaData = source.MetaData.DeepClone(); + this.Metadata = source.Metadata.DeepClone(); } /// @@ -169,9 +169,9 @@ namespace SixLabors.ImageSharp public int Height => this.PixelBuffer.Height; /// - /// Gets the meta data of the frame. + /// Gets the metadata of the frame. /// - public ImageFrameMetaData MetaData { get; } + public ImageFrameMetadata Metadata { get; } /// /// Gets or sets the pixel at the specified position. @@ -289,7 +289,7 @@ namespace SixLabors.ImageSharp return this.Clone(configuration) as ImageFrame; } - var target = new ImageFrame(configuration, this.Width, this.Height, this.MetaData.DeepClone()); + var target = new ImageFrame(configuration, this.Width, this.Height, this.Metadata.DeepClone()); ParallelHelper.IterateRows( this.Bounds(), diff --git a/src/ImageSharp/ImageInfo.cs b/src/ImageSharp/ImageInfo.cs index 6f894cb599..12dcf1ed74 100644 --- a/src/ImageSharp/ImageInfo.cs +++ b/src/ImageSharp/ImageInfo.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Formats; -using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.Metadata; namespace SixLabors.ImageSharp { @@ -17,13 +17,13 @@ namespace SixLabors.ImageSharp /// The image pixel type information. /// The width of the image in pixels. /// The height of the image in pixels. - /// The images metadata. - public ImageInfo(PixelTypeInfo pixelType, int width, int height, ImageMetaData metaData) + /// The images metadata. + public ImageInfo(PixelTypeInfo pixelType, int width, int height, ImageMetadata metadata) { this.PixelType = pixelType; this.Width = width; this.Height = height; - this.MetaData = metaData; + this.Metadata = metadata; } /// @@ -36,6 +36,6 @@ namespace SixLabors.ImageSharp public int Height { get; } /// - public ImageMetaData MetaData { get; } + public ImageMetadata Metadata { get; } } } \ No newline at end of file diff --git a/src/ImageSharp/Image{TPixel}.cs b/src/ImageSharp/Image{TPixel}.cs index 178194b21e..f2bef78e1a 100644 --- a/src/ImageSharp/Image{TPixel}.cs +++ b/src/ImageSharp/Image{TPixel}.cs @@ -9,7 +9,7 @@ using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp @@ -31,7 +31,7 @@ namespace SixLabors.ImageSharp /// The width of the image in pixels. /// The height of the image in pixels. public Image(Configuration configuration, int width, int height) - : this(configuration, width, height, new ImageMetaData()) + : this(configuration, width, height, new ImageMetadata()) { } @@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp /// The height of the image in pixels. /// The color to initialize the pixels with. public Image(Configuration configuration, int width, int height, TPixel backgroundColor) - : this(configuration, width, height, backgroundColor, new ImageMetaData()) + : this(configuration, width, height, backgroundColor, new ImageMetadata()) { } @@ -67,11 +67,11 @@ namespace SixLabors.ImageSharp /// The width of the image in pixels. /// The height of the image in pixels. /// The images metadata. - internal Image(Configuration configuration, int width, int height, ImageMetaData metadata) + internal Image(Configuration configuration, int width, int height, ImageMetadata metadata) { this.configuration = configuration ?? Configuration.Default; this.PixelType = new PixelTypeInfo(Unsafe.SizeOf() * 8); - this.MetaData = metadata ?? new ImageMetaData(); + this.Metadata = metadata ?? new ImageMetadata(); this.Frames = new ImageFrameCollection(this, width, height, default(TPixel)); } @@ -84,11 +84,11 @@ namespace SixLabors.ImageSharp /// The width of the image in pixels. /// The height of the image in pixels. /// The images metadata. - internal Image(Configuration configuration, MemorySource memorySource, int width, int height, ImageMetaData metadata) + internal Image(Configuration configuration, MemorySource memorySource, int width, int height, ImageMetadata metadata) { this.configuration = configuration; this.PixelType = new PixelTypeInfo(Unsafe.SizeOf() * 8); - this.MetaData = metadata; + this.Metadata = metadata; this.Frames = new ImageFrameCollection(this, width, height, memorySource); } @@ -101,11 +101,11 @@ namespace SixLabors.ImageSharp /// The height of the image in pixels. /// The color to initialize the pixels with. /// The images metadata. - internal Image(Configuration configuration, int width, int height, TPixel backgroundColor, ImageMetaData metadata) + internal Image(Configuration configuration, int width, int height, TPixel backgroundColor, ImageMetadata metadata) { this.configuration = configuration ?? Configuration.Default; this.PixelType = new PixelTypeInfo(Unsafe.SizeOf() * 8); - this.MetaData = metadata ?? new ImageMetaData(); + this.Metadata = metadata ?? new ImageMetadata(); this.Frames = new ImageFrameCollection(this, width, height, backgroundColor); } @@ -116,11 +116,11 @@ namespace SixLabors.ImageSharp /// The configuration providing initialization code which allows extending the library. /// The images metadata. /// The frames that will be owned by this image instance. - internal Image(Configuration configuration, ImageMetaData metadata, IEnumerable> frames) + internal Image(Configuration configuration, ImageMetadata metadata, IEnumerable> frames) { this.configuration = configuration ?? Configuration.Default; this.PixelType = new PixelTypeInfo(Unsafe.SizeOf() * 8); - this.MetaData = metadata ?? new ImageMetaData(); + this.Metadata = metadata ?? new ImageMetadata(); this.Frames = new ImageFrameCollection(this, frames); } @@ -140,7 +140,7 @@ namespace SixLabors.ImageSharp public int Height => this.Frames.RootFrame.Height; /// - public ImageMetaData MetaData { get; } + public ImageMetadata Metadata { get; } /// /// Gets the frames. @@ -193,7 +193,7 @@ namespace SixLabors.ImageSharp public Image Clone(Configuration configuration) { IEnumerable> clonedFrames = this.Frames.Select(x => x.Clone(configuration)); - return new Image(configuration, this.MetaData.DeepClone(), clonedFrames); + return new Image(configuration, this.Metadata.DeepClone(), clonedFrames); } /// @@ -214,7 +214,7 @@ namespace SixLabors.ImageSharp where TPixel2 : struct, IPixel { IEnumerable> clonedFrames = this.Frames.Select(x => x.CloneAs(configuration)); - return new Image(configuration, this.MetaData.DeepClone(), clonedFrames); + return new Image(configuration, this.Metadata.DeepClone(), clonedFrames); } /// diff --git a/src/ImageSharp/MetaData/FrameDecodingMode.cs b/src/ImageSharp/MetaData/FrameDecodingMode.cs index 2863fbf8f9..835e43354a 100644 --- a/src/ImageSharp/MetaData/FrameDecodingMode.cs +++ b/src/ImageSharp/MetaData/FrameDecodingMode.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.MetaData +namespace SixLabors.ImageSharp.Metadata { /// /// Enumerated frame process modes to apply to multi-frame images. diff --git a/src/ImageSharp/MetaData/ImageFrameMetaData.cs b/src/ImageSharp/MetaData/ImageFrameMetaData.cs index f1f884be68..3858a7d0a3 100644 --- a/src/ImageSharp/MetaData/ImageFrameMetaData.cs +++ b/src/ImageSharp/MetaData/ImageFrameMetaData.cs @@ -4,62 +4,62 @@ using System.Collections.Generic; using SixLabors.ImageSharp.Formats; -namespace SixLabors.ImageSharp.MetaData +namespace SixLabors.ImageSharp.Metadata { /// /// Encapsulates the metadata of an image frame. /// - public sealed class ImageFrameMetaData : IDeepCloneable + public sealed class ImageFrameMetadata : IDeepCloneable { - private readonly Dictionary formatMetaData = new Dictionary(); + private readonly Dictionary formatMetadata = new Dictionary(); /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - internal ImageFrameMetaData() + internal ImageFrameMetadata() { } /// - /// Initializes a new instance of the class + /// Initializes a new instance of the class /// by making a copy from other metadata. /// /// - /// The other to create this instance from. + /// The other to create this instance from. /// - internal ImageFrameMetaData(ImageFrameMetaData other) + internal ImageFrameMetadata(ImageFrameMetadata other) { DebugGuard.NotNull(other, nameof(other)); - foreach (KeyValuePair meta in other.formatMetaData) + foreach (KeyValuePair meta in other.formatMetadata) { - this.formatMetaData.Add(meta.Key, meta.Value.DeepClone()); + this.formatMetadata.Add(meta.Key, meta.Value.DeepClone()); } } /// - public ImageFrameMetaData DeepClone() => new ImageFrameMetaData(this); + public ImageFrameMetadata DeepClone() => new ImageFrameMetadata(this); /// /// Gets the metadata value associated with the specified key. /// - /// The type of format metadata. - /// The type of format frame metadata. + /// The type of format metadata. + /// The type of format frame metadata. /// The key of the value to get. /// - /// The . + /// The . /// - public TFormatFrameMetaData GetFormatMetaData(IImageFormat key) - where TFormatMetaData : class - where TFormatFrameMetaData : class, IDeepCloneable + public TFormatFrameMetadata GetFormatMetadata(IImageFormat key) + where TFormatMetadata : class + where TFormatFrameMetadata : class, IDeepCloneable { - if (this.formatMetaData.TryGetValue(key, out IDeepCloneable meta)) + if (this.formatMetadata.TryGetValue(key, out IDeepCloneable meta)) { - return (TFormatFrameMetaData)meta; + return (TFormatFrameMetadata)meta; } - TFormatFrameMetaData newMeta = key.CreateDefaultFormatFrameMetaData(); - this.formatMetaData[key] = newMeta; + TFormatFrameMetadata newMeta = key.CreateDefaultFormatFrameMetadata(); + this.formatMetadata[key] = newMeta; return newMeta; } } diff --git a/src/ImageSharp/MetaData/ImageMetaData.cs b/src/ImageSharp/MetaData/ImageMetaData.cs index 73549d98aa..b9efca4fee 100644 --- a/src/ImageSharp/MetaData/ImageMetaData.cs +++ b/src/ImageSharp/MetaData/ImageMetaData.cs @@ -3,15 +3,15 @@ using System.Collections.Generic; using SixLabors.ImageSharp.Formats; -using SixLabors.ImageSharp.MetaData.Profiles.Exif; -using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using SixLabors.ImageSharp.Metadata.Profiles.Exif; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; -namespace SixLabors.ImageSharp.MetaData +namespace SixLabors.ImageSharp.Metadata { /// /// Encapsulates the metadata of an image. /// - public sealed class ImageMetaData : IDeepCloneable + public sealed class ImageMetadata : IDeepCloneable { /// /// The default horizontal resolution value (dots per inch) in x direction. @@ -31,14 +31,14 @@ namespace SixLabors.ImageSharp.MetaData /// public const PixelResolutionUnit DefaultPixelResolutionUnits = PixelResolutionUnit.PixelsPerInch; - private readonly Dictionary formatMetaData = new Dictionary(); + private readonly Dictionary formatMetadata = new Dictionary(); private double horizontalResolution; private double verticalResolution; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - internal ImageMetaData() + internal ImageMetadata() { this.horizontalResolution = DefaultHorizontalResolution; this.verticalResolution = DefaultVerticalResolution; @@ -46,21 +46,21 @@ namespace SixLabors.ImageSharp.MetaData } /// - /// Initializes a new instance of the class + /// Initializes a new instance of the class /// by making a copy from other metadata. /// /// - /// The other to create this instance from. + /// The other to create this instance from. /// - private ImageMetaData(ImageMetaData other) + private ImageMetadata(ImageMetadata other) { this.HorizontalResolution = other.HorizontalResolution; this.VerticalResolution = other.VerticalResolution; this.ResolutionUnits = other.ResolutionUnits; - foreach (KeyValuePair meta in other.formatMetaData) + foreach (KeyValuePair meta in other.formatMetadata) { - this.formatMetaData.Add(meta.Key, meta.Value.DeepClone()); + this.formatMetadata.Add(meta.Key, meta.Value.DeepClone()); } foreach (ImageProperty property in other.Properties) @@ -135,26 +135,26 @@ namespace SixLabors.ImageSharp.MetaData /// /// Gets the metadata value associated with the specified key. /// - /// The type of metadata. + /// The type of metadata. /// The key of the value to get. /// - /// The . + /// The . /// - public TFormatMetaData GetFormatMetaData(IImageFormat key) - where TFormatMetaData : class, IDeepCloneable + public TFormatMetadata GetFormatMetadata(IImageFormat key) + where TFormatMetadata : class, IDeepCloneable { - if (this.formatMetaData.TryGetValue(key, out IDeepCloneable meta)) + if (this.formatMetadata.TryGetValue(key, out IDeepCloneable meta)) { - return (TFormatMetaData)meta; + return (TFormatMetadata)meta; } - TFormatMetaData newMeta = key.CreateDefaultFormatMetaData(); - this.formatMetaData[key] = newMeta; + TFormatMetadata newMeta = key.CreateDefaultFormatMetadata(); + this.formatMetadata[key] = newMeta; return newMeta; } /// - public ImageMetaData DeepClone() => new ImageMetaData(this); + public ImageMetadata DeepClone() => new ImageMetadata(this); /// /// Looks up a property with the provided name. @@ -180,7 +180,7 @@ namespace SixLabors.ImageSharp.MetaData } /// - /// Synchronizes the profiles with the current meta data. + /// Synchronizes the profiles with the current metadata. /// internal void SyncProfiles() => this.ExifProfile?.Sync(this); } diff --git a/src/ImageSharp/MetaData/ImageProperty.cs b/src/ImageSharp/MetaData/ImageProperty.cs index 6d38b6bf13..905e42dab1 100644 --- a/src/ImageSharp/MetaData/ImageProperty.cs +++ b/src/ImageSharp/MetaData/ImageProperty.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.MetaData +namespace SixLabors.ImageSharp.Metadata { /// /// Stores meta information about a image, like the name of the author, diff --git a/src/ImageSharp/MetaData/PixelResolutionUnit.cs b/src/ImageSharp/MetaData/PixelResolutionUnit.cs index ce848004f7..661e7a308e 100644 --- a/src/ImageSharp/MetaData/PixelResolutionUnit.cs +++ b/src/ImageSharp/MetaData/PixelResolutionUnit.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.MetaData +namespace SixLabors.ImageSharp.Metadata { /// /// Provides enumeration of available pixel density units. diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifConstants.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifConstants.cs index fb2a893d1c..c7112c47a3 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifConstants.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifConstants.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.MetaData.Profiles.Exif +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif { internal static class ExifConstants { diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifDataType.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifDataType.cs index 5bd38b195d..83e7f7fe8b 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifDataType.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifDataType.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.MetaData.Profiles.Exif +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif { /// /// Specifies exif data types. diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifParts.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifParts.cs index b1b42ad433..d22dc730f9 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifParts.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifParts.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.MetaData.Profiles.Exif +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif { /// /// Specifies which parts will be written when the profile is added to an image. diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs index 37ceaf10f6..3d90cb3a9e 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs @@ -7,7 +7,7 @@ using System.IO; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; -namespace SixLabors.ImageSharp.MetaData.Profiles.Exif +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif { /// /// Represents an EXIF profile providing access to the collection of values. @@ -249,13 +249,13 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif public ExifProfile DeepClone() => new ExifProfile(this); /// - /// Synchronizes the profiles with the specified meta data. + /// Synchronizes the profiles with the specified metadata. /// - /// The meta data. - internal void Sync(ImageMetaData metaData) + /// The metadata. + internal void Sync(ImageMetadata metadata) { - this.SyncResolution(ExifTag.XResolution, metaData.HorizontalResolution); - this.SyncResolution(ExifTag.YResolution, metaData.VerticalResolution); + this.SyncResolution(ExifTag.XResolution, metadata.HorizontalResolution); + this.SyncResolution(ExifTag.YResolution, metadata.VerticalResolution); } private void SyncResolution(ExifTag tag, double resolution) diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs index cf95affb04..e40e6c26c2 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs @@ -10,7 +10,7 @@ using System.Runtime.CompilerServices; using System.Text; using SixLabors.ImageSharp.Primitives; -namespace SixLabors.ImageSharp.MetaData.Profiles.Exif +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif { /// /// Reads and parses EXIF data from a byte array. diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifTag.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifTag.cs index 625f95b63d..ddd4591fac 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifTag.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifTag.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.MetaData.Profiles.Exif +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif { /// /// All exif tags from the Exif standard 2.2 diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifTagDescriptionAttribute.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifTagDescriptionAttribute.cs index 286fdbe57f..0188c662ed 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifTagDescriptionAttribute.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifTagDescriptionAttribute.cs @@ -4,7 +4,7 @@ using System; using System.Reflection; -namespace SixLabors.ImageSharp.MetaData.Profiles.Exif +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif { /// /// Class that provides a description for an ExifTag value. diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifTags.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifTags.cs index e999d00b86..0ed3a43b70 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifTags.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifTags.cs @@ -1,9 +1,9 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using static SixLabors.ImageSharp.MetaData.Profiles.Exif.ExifTag; +using static SixLabors.ImageSharp.Metadata.Profiles.Exif.ExifTag; -namespace SixLabors.ImageSharp.MetaData.Profiles.Exif +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif { internal static class ExifTags { diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs index 409c55253a..05a9f35c9b 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs @@ -6,7 +6,7 @@ using System.Globalization; using System.Text; using SixLabors.ImageSharp.Primitives; -namespace SixLabors.ImageSharp.MetaData.Profiles.Exif +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif { /// /// Represent the value of the EXIF profile. diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs index 9079526d5a..93fe7fbbee 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs @@ -7,7 +7,7 @@ using System.Collections.Generic; using System.Text; using SixLabors.ImageSharp.Primitives; -namespace SixLabors.ImageSharp.MetaData.Profiles.Exif +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif { /// /// Contains methods for writing EXIF metadata. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccCurveSegment.cs b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccCurveSegment.cs index 516887bcd6..7bf1f84211 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccCurveSegment.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccCurveSegment.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// A segment of a curve diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccFormulaCurveElement.cs b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccFormulaCurveElement.cs index d168c5c285..d013f6ef13 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccFormulaCurveElement.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccFormulaCurveElement.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// A formula based curve segment diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccOneDimensionalCurve.cs b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccOneDimensionalCurve.cs index 7076e51447..e1d362a7fe 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccOneDimensionalCurve.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccOneDimensionalCurve.cs @@ -4,7 +4,7 @@ using System; using System.Linq; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// A one dimensional ICC curve. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccParametricCurve.cs b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccParametricCurve.cs index efc2ca5377..04a9faf7da 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccParametricCurve.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccParametricCurve.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// A parametric curve diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccResponseCurve.cs b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccResponseCurve.cs index de08485ac8..4f0345d352 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccResponseCurve.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccResponseCurve.cs @@ -5,7 +5,7 @@ using System; using System.Linq; using System.Numerics; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// A response curve diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccSampledCurveElement.cs b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccSampledCurveElement.cs index d9badf5a80..4872bd2b08 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccSampledCurveElement.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccSampledCurveElement.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// A sampled curve segment diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Curves.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Curves.cs index b27083dc49..111911080e 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Curves.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Curves.cs @@ -3,7 +3,7 @@ using System.Numerics; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Provides methods to read ICC data types diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Lut.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Lut.cs index f9d56b0fff..5262e3ee95 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Lut.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Lut.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Provides methods to read ICC data types diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Matrix.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Matrix.cs index 495d73a784..3157b9ef38 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Matrix.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Matrix.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Provides methods to read ICC data types diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.MultiProcessElement.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.MultiProcessElement.cs index 2b39b1d6c6..4253058288 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.MultiProcessElement.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.MultiProcessElement.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Provides methods to read ICC data types diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.NonPrimitives.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.NonPrimitives.cs index 7dc8cf98aa..aacbbcc1dd 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.NonPrimitives.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.NonPrimitives.cs @@ -4,7 +4,7 @@ using System; using System.Numerics; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Provides methods to read ICC data types diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Primitives.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Primitives.cs index bb85a5ca3e..2cbf798ec2 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Primitives.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Primitives.cs @@ -6,7 +6,7 @@ using System.Buffers.Binary; using System.Runtime.CompilerServices; using System.Text; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Provides methods to read ICC data types diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs index c572b7f210..3f330ef729 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs @@ -5,7 +5,7 @@ using System; using System.Globalization; using System.Numerics; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Provides methods to read ICC data types diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.cs index 91a28fd743..7d694bec6e 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.cs @@ -4,7 +4,7 @@ using System; using System.Text; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Provides methods to read ICC data types diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Curves.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Curves.cs index d5f5d5a9b6..c48ba8ab7c 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Curves.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Curves.cs @@ -3,7 +3,7 @@ using System.Numerics; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Provides methods to write ICC data types diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Lut.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Lut.cs index 38a3dd457d..016bd8009a 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Lut.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Lut.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Provides methods to write ICC data types diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Matrix.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Matrix.cs index 79b9132192..b0bd377cb3 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Matrix.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Matrix.cs @@ -4,7 +4,7 @@ using System.Numerics; using SixLabors.ImageSharp.Primitives; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Provides methods to write ICC data types diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MultiProcessElement.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MultiProcessElement.cs index 48c6734aec..824a9bef61 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MultiProcessElement.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MultiProcessElement.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Provides methods to write ICC data types diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.NonPrimitives.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.NonPrimitives.cs index 1a3c2c0ac5..e681f84b85 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.NonPrimitives.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.NonPrimitives.cs @@ -4,7 +4,7 @@ using System; using System.Numerics; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Provides methods to write ICC data types diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Primitives.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Primitives.cs index 404285b500..6c49eb0c43 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Primitives.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Primitives.cs @@ -4,7 +4,7 @@ using System; using System.Text; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Provides methods to write ICC data types diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntry.cs index 51ea2d4e4a..13fb023d38 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntry.cs @@ -3,7 +3,7 @@ using System.Linq; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Provides methods to write ICC data types diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.cs index 21b7b6421b..17f15df714 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.cs @@ -4,7 +4,7 @@ using System; using System.IO; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Provides methods to write ICC data types diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccClutDataType.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccClutDataType.cs index fe0c9b8b59..71e68f6af1 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccClutDataType.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccClutDataType.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Color lookup table data type diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccColorSpaceType.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccColorSpaceType.cs index 0913cda418..721545df36 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccColorSpaceType.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccColorSpaceType.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Color Space Type diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccColorantEncoding.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccColorantEncoding.cs index bec71e0dbc..14b2dd960f 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccColorantEncoding.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccColorantEncoding.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Colorant Encoding diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccCurveMeasurementEncodings.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccCurveMeasurementEncodings.cs index cfd2671e94..a620632da6 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccCurveMeasurementEncodings.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccCurveMeasurementEncodings.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Curve Measurement Encodings diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccCurveSegmentSignature.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccCurveSegmentSignature.cs index e9b8cc26b8..9a5ae18052 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccCurveSegmentSignature.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccCurveSegmentSignature.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Curve Segment Signature diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccDataType.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccDataType.cs index b13edb53f4..de1f116366 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccDataType.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccDataType.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Enumerates the basic data types as defined in ICC.1:2010 version 4.3.0.0 diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccDeviceAttribute.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccDeviceAttribute.cs index 88cca866dc..c8598b0e03 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccDeviceAttribute.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccDeviceAttribute.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Device attributes. Can be combined with a logical OR diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccFormulaCurveType.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccFormulaCurveType.cs index 2a375b0d1c..11e5985af3 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccFormulaCurveType.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccFormulaCurveType.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Formula curve segment type diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccMeasurementGeometry.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccMeasurementGeometry.cs index fe6575c41b..9373241949 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccMeasurementGeometry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccMeasurementGeometry.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Measurement Geometry diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccMultiProcessElementSignature.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccMultiProcessElementSignature.cs index fe5d309228..d7f78889dc 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccMultiProcessElementSignature.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccMultiProcessElementSignature.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Multi process element signature diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccParametricCurveType.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccParametricCurveType.cs index 374b4ad93b..fce08a3afe 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccParametricCurveType.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccParametricCurveType.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Formula curve segment type diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccPrimaryPlatformType.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccPrimaryPlatformType.cs index 1563078f72..035fd3d4d2 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccPrimaryPlatformType.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccPrimaryPlatformType.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Enumerates the primary platform/operating system framework for which the profile was created diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileClass.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileClass.cs index 8f0427db27..3d59192a7b 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileClass.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileClass.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Profile Class Name diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileFlag.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileFlag.cs index 1e9ec18e89..9fbe5b5b5a 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileFlag.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileFlag.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Profile flags. Can be combined with a logical OR. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileTag.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileTag.cs index 61d34dca10..b19641e0fb 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileTag.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileTag.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. // ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Enumerates the ICC Profile Tags as defined in ICC.1:2010 version 4.3.0.0 diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccRenderingIntent.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccRenderingIntent.cs index 7cb9c00f39..8ae241b448 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccRenderingIntent.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccRenderingIntent.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Rendering intent diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccScreeningFlag.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccScreeningFlag.cs index 839ab8c6b1..b43ad52c48 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccScreeningFlag.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccScreeningFlag.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Screening flags. Can be combined with a logical OR. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccScreeningSpotType.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccScreeningSpotType.cs index f1d73c6268..0631892b62 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccScreeningSpotType.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccScreeningSpotType.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Enumerates the screening spot types diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccSignatureName.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccSignatureName.cs index 4773ab6c2a..f93d22f3e4 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccSignatureName.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccSignatureName.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Signature Name diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccStandardIlluminant.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccStandardIlluminant.cs index fe0c406576..fc8ebae5cd 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccStandardIlluminant.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccStandardIlluminant.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Standard Illuminant diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccStandardObserver.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccStandardObserver.cs index a393c258b2..1454b28273 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccStandardObserver.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccStandardObserver.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Standard Observer diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccTypeSignature.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccTypeSignature.cs index ad0db4df93..d7a18579e5 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccTypeSignature.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccTypeSignature.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Type Signature diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Exceptions/InvalidIccProfileException.cs b/src/ImageSharp/MetaData/Profiles/ICC/Exceptions/InvalidIccProfileException.cs index f690670419..019bf9202c 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Exceptions/InvalidIccProfileException.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Exceptions/InvalidIccProfileException.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Represents an error that happened while reading or writing a corrupt/invalid ICC profile diff --git a/src/ImageSharp/MetaData/Profiles/ICC/IccProfile.cs b/src/ImageSharp/MetaData/Profiles/ICC/IccProfile.cs index 5d75a6df9d..afdd7ebfb5 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/IccProfile.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/IccProfile.cs @@ -4,7 +4,7 @@ using System; using System.Security.Cryptography; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Represents an ICC profile diff --git a/src/ImageSharp/MetaData/Profiles/ICC/IccProfileHeader.cs b/src/ImageSharp/MetaData/Profiles/ICC/IccProfileHeader.cs index 189b40275a..326dd351e7 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/IccProfileHeader.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/IccProfileHeader.cs @@ -4,7 +4,7 @@ using System; using System.Numerics; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Contains all values of an ICC profile header. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/IccReader.cs b/src/ImageSharp/MetaData/Profiles/ICC/IccReader.cs index 9f9d373ae1..30a6de40d0 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/IccReader.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/IccReader.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Reads and parses ICC data from a byte array diff --git a/src/ImageSharp/MetaData/Profiles/ICC/IccTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/IccTagDataEntry.cs index 4c6bb07858..658d4c2f1b 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/IccTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/IccTagDataEntry.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// The data of an ICC tag entry diff --git a/src/ImageSharp/MetaData/Profiles/ICC/IccWriter.cs b/src/ImageSharp/MetaData/Profiles/ICC/IccWriter.cs index 91a3bba549..b5a7c830dd 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/IccWriter.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/IccWriter.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using System.Linq; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Contains methods for writing ICC profiles. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccBAcsProcessElement.cs b/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccBAcsProcessElement.cs index 09931a1f91..c825a35352 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccBAcsProcessElement.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccBAcsProcessElement.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// A placeholder (might be used for future ICC versions) diff --git a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccClutProcessElement.cs b/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccClutProcessElement.cs index 6aba186326..7ee7f821d7 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccClutProcessElement.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccClutProcessElement.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// A CLUT (color lookup table) element to process data diff --git a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccCurveSetProcessElement.cs b/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccCurveSetProcessElement.cs index 7585fc2128..d72aff3c92 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccCurveSetProcessElement.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccCurveSetProcessElement.cs @@ -4,7 +4,7 @@ using System; using System.Linq; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// A set of curves to process data diff --git a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccEAcsProcessElement.cs b/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccEAcsProcessElement.cs index 7e0e1eda82..d6f42aea76 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccEAcsProcessElement.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccEAcsProcessElement.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// A placeholder (might be used for future ICC versions) diff --git a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccMatrixProcessElement.cs b/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccMatrixProcessElement.cs index e6170f7680..0d8683397a 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccMatrixProcessElement.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccMatrixProcessElement.cs @@ -5,7 +5,7 @@ using System; using SixLabors.ImageSharp.Primitives; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// A matrix element to process data diff --git a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccMultiProcessElement.cs b/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccMultiProcessElement.cs index db2d56cc3d..24649d8b50 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccMultiProcessElement.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccMultiProcessElement.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// An element to process data diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccChromaticityTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccChromaticityTagDataEntry.cs index e9a812d8ca..b4d711f59e 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccChromaticityTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccChromaticityTagDataEntry.cs @@ -4,7 +4,7 @@ using System; using System.Linq; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// The chromaticity tag type provides basic chromaticity data diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantOrderTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantOrderTagDataEntry.cs index b5f8fd5c49..4136b9a389 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantOrderTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantOrderTagDataEntry.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// This tag specifies the laydown order in which colorants diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantTableTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantTableTagDataEntry.cs index 9f2b4c90ad..28d01450c1 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantTableTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantTableTagDataEntry.cs @@ -4,7 +4,7 @@ using System; using System.Linq; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// The purpose of this tag is to identify the colorants used in diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCrdInfoTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCrdInfoTagDataEntry.cs index 4d393dfb33..bcf9d5c9e1 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCrdInfoTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCrdInfoTagDataEntry.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// This type contains the PostScript product name to which this profile diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCurveTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCurveTagDataEntry.cs index 0d34d3ceba..f2205cbad5 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCurveTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCurveTagDataEntry.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// The type contains a one-dimensional table of double values. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDataTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDataTagDataEntry.cs index 0b8367303d..6a0b6c05e8 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDataTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDataTagDataEntry.cs @@ -4,7 +4,7 @@ using System; using System.Text; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// The dataType is a simple data structure that contains diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDateTimeTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDateTimeTagDataEntry.cs index 104d243099..371397a6f8 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDateTimeTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDateTimeTagDataEntry.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// This type is a representation of the time and date. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccFix16ArrayTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccFix16ArrayTagDataEntry.cs index 0a114ea1f9..50bfca1756 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccFix16ArrayTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccFix16ArrayTagDataEntry.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// This type represents an array of doubles (from 32bit fixed point values). diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut16TagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut16TagDataEntry.cs index 415f6941ec..d84fc1431e 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut16TagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut16TagDataEntry.cs @@ -5,7 +5,7 @@ using System; using System.Linq; using System.Numerics; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// This structure represents a color transform using tables diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut8TagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut8TagDataEntry.cs index 04a49e316d..5c8ce2d2c9 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut8TagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut8TagDataEntry.cs @@ -5,7 +5,7 @@ using System; using System.Linq; using System.Numerics; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// This structure represents a color transform using tables diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutAToBTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutAToBTagDataEntry.cs index f7c0946320..4cee555e51 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutAToBTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutAToBTagDataEntry.cs @@ -5,7 +5,7 @@ using System; using System.Linq; using System.Numerics; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// This structure represents a color transform. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutBToATagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutBToATagDataEntry.cs index 27572acd0b..8e9479ff83 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutBToATagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutBToATagDataEntry.cs @@ -5,7 +5,7 @@ using System; using System.Linq; using System.Numerics; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// This structure represents a color transform. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMeasurementTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMeasurementTagDataEntry.cs index 9247ca593f..ad3a9f2c36 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMeasurementTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMeasurementTagDataEntry.cs @@ -4,7 +4,7 @@ using System; using System.Numerics; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// The measurementType information refers only to the internal diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiLocalizedUnicodeTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiLocalizedUnicodeTagDataEntry.cs index 1a06eb5880..3084ff6121 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiLocalizedUnicodeTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiLocalizedUnicodeTagDataEntry.cs @@ -4,7 +4,7 @@ using System; using System.Linq; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// This tag structure contains a set of records each referencing diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiProcessElementsTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiProcessElementsTagDataEntry.cs index f13fdb17fb..dc78e75200 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiProcessElementsTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiProcessElementsTagDataEntry.cs @@ -4,7 +4,7 @@ using System; using System.Linq; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// This structure represents a color transform, containing diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccNamedColor2TagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccNamedColor2TagDataEntry.cs index 957b8d5c3f..30516578fe 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccNamedColor2TagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccNamedColor2TagDataEntry.cs @@ -4,7 +4,7 @@ using System; using System.Linq; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// The namedColor2Type is a count value and array of structures diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccParametricCurveTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccParametricCurveTagDataEntry.cs index 9ec497d3be..752d05aae2 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccParametricCurveTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccParametricCurveTagDataEntry.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// The parametricCurveType describes a one-dimensional curve by diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceDescTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceDescTagDataEntry.cs index ff69115267..3c3a6c7619 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceDescTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceDescTagDataEntry.cs @@ -4,7 +4,7 @@ using System; using System.Linq; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// This type is an array of structures, each of which contains information diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceIdentifierTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceIdentifierTagDataEntry.cs index c6cc0903c5..06e52cb637 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceIdentifierTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceIdentifierTagDataEntry.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// This type is an array of structures, each of which contains information diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccResponseCurveSet16TagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccResponseCurveSet16TagDataEntry.cs index 494aa2888b..6051bf8414 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccResponseCurveSet16TagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccResponseCurveSet16TagDataEntry.cs @@ -4,7 +4,7 @@ using System; using System.Linq; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// The purpose of this tag type is to provide a mechanism to relate physical diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccScreeningTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccScreeningTagDataEntry.cs index a073291ad1..bf64e4fed0 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccScreeningTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccScreeningTagDataEntry.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// This type describes various screening parameters including diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccSignatureTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccSignatureTagDataEntry.cs index 287f0efb0c..87d369f85b 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccSignatureTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccSignatureTagDataEntry.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Typically this type is used for registered tags that can diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextDescriptionTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextDescriptionTagDataEntry.cs index 8e6f4bc196..4134779c3c 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextDescriptionTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextDescriptionTagDataEntry.cs @@ -4,7 +4,7 @@ using System; using System.Globalization; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// The TextDescriptionType contains three types of text description. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextTagDataEntry.cs index ab3b3fb6fb..6cf9e91543 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextTagDataEntry.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// This is a simple text structure that contains a text string. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUFix16ArrayTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUFix16ArrayTagDataEntry.cs index 464cbb9e75..3e4a5ff3a9 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUFix16ArrayTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUFix16ArrayTagDataEntry.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// This type represents an array of doubles (from 32bit values). diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt16ArrayTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt16ArrayTagDataEntry.cs index 1e7a7f8a90..46799b16a9 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt16ArrayTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt16ArrayTagDataEntry.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// This type represents an array of unsigned shorts. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt32ArrayTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt32ArrayTagDataEntry.cs index affdc8720b..9fbbf8bf57 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt32ArrayTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt32ArrayTagDataEntry.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// This type represents an array of unsigned 32bit integers. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt64ArrayTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt64ArrayTagDataEntry.cs index 36d48d6135..053181a26f 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt64ArrayTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt64ArrayTagDataEntry.cs @@ -4,7 +4,7 @@ using System; using System.Linq; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// This type represents an array of unsigned 64bit integers. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt8ArrayTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt8ArrayTagDataEntry.cs index b2f2677575..099916d894 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt8ArrayTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt8ArrayTagDataEntry.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// This type represents an array of bytes. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUcrBgTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUcrBgTagDataEntry.cs index 510930e397..d28b6edc96 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUcrBgTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUcrBgTagDataEntry.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// This type contains curves representing the under color removal and black generation diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUnknownTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUnknownTagDataEntry.cs index a0089e7359..0227983fa1 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUnknownTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUnknownTagDataEntry.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// This tag stores data of an unknown tag data entry diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccViewingConditionsTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccViewingConditionsTagDataEntry.cs index bd636d4f2c..2b2893c388 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccViewingConditionsTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccViewingConditionsTagDataEntry.cs @@ -4,7 +4,7 @@ using System; using System.Numerics; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// This type represents a set of viewing condition parameters. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccXyzTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccXyzTagDataEntry.cs index c1c14d8cbb..505b73a89f 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccXyzTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccXyzTagDataEntry.cs @@ -4,7 +4,7 @@ using System; using System.Numerics; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// The XYZType contains an array of XYZ values. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccClut.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccClut.cs index 3f7cad3afe..c53ba95201 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccClut.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccClut.cs @@ -4,7 +4,7 @@ using System; using System.Linq; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Color Lookup Table diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccColorantTableEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccColorantTableEntry.cs index 4bcd1b6861..db1feea9ab 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccColorantTableEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccColorantTableEntry.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Entry of ICC colorant table diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccLocalizedString.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccLocalizedString.cs index 1920e97fe1..b2852c8a33 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccLocalizedString.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccLocalizedString.cs @@ -4,7 +4,7 @@ using System; using System.Globalization; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// A string with a specific locale. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccLut.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccLut.cs index c46d6884b6..9e0c6c40dd 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccLut.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccLut.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Lookup Table diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccNamedColor.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccNamedColor.cs index b7cb5bc495..22bf1bb762 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccNamedColor.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccNamedColor.cs @@ -4,7 +4,7 @@ using System; using System.Linq; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// A specific color with a name diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccPositionNumber.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccPositionNumber.cs index 745312f563..c9b75903d9 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccPositionNumber.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccPositionNumber.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Position of an object within an ICC profile diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileDescription.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileDescription.cs index 7e7c527ead..76ac5961d9 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileDescription.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileDescription.cs @@ -4,7 +4,7 @@ using System; using System.Linq; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// ICC Profile description diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileId.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileId.cs index 710e6bb252..4a73f7e079 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileId.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileId.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// ICC Profile ID diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileSequenceIdentifier.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileSequenceIdentifier.cs index ae451c5db8..9eb9fc7c3e 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileSequenceIdentifier.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileSequenceIdentifier.cs @@ -4,7 +4,7 @@ using System; using System.Linq; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Description of a profile within a sequence. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccResponseNumber.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccResponseNumber.cs index 8cae869925..8590802638 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccResponseNumber.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccResponseNumber.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Associates a normalized device code with a measurement value diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccScreeningChannel.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccScreeningChannel.cs index 7e2072d5bb..354442b476 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccScreeningChannel.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccScreeningChannel.cs @@ -4,7 +4,7 @@ using System; using System.Runtime.InteropServices; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// A single channel of a diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccTagTableEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccTagTableEntry.cs index 7713c4faa1..889dec41eb 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccTagTableEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccTagTableEntry.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Entry of ICC tag table diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccVersion.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccVersion.cs index 2486cc80a9..b388fa5a48 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccVersion.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccVersion.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Represents the ICC profile version number. diff --git a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs index 0f71c732dd..7633ed4418 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs @@ -53,10 +53,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms { // We will always be creating the clone even for mutate because we may need to resize the canvas IEnumerable> frames = - source.Frames.Select(x => new ImageFrame(source.GetConfiguration(), this.TargetDimensions, x.MetaData.DeepClone())); + source.Frames.Select(x => new ImageFrame(source.GetConfiguration(), this.TargetDimensions, x.Metadata.DeepClone())); // Use the overload to prevent an extra frame being added - return new Image(source.GetConfiguration(), source.MetaData.DeepClone(), frames); + return new Image(source.GetConfiguration(), source.Metadata.DeepClone(), frames); } /// diff --git a/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs index a610ae5bb3..5b9e3dde23 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; -using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors; using SixLabors.Primitives; @@ -73,12 +73,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// The private static OrientationMode GetExifOrientation(Image source) { - if (source.MetaData.ExifProfile is null) + if (source.Metadata.ExifProfile is null) { return OrientationMode.Unknown; } - ExifValue value = source.MetaData.ExifProfile.GetValue(ExifTag.Orientation); + ExifValue value = source.Metadata.ExifProfile.GetValue(ExifTag.Orientation); if (value is null) { return OrientationMode.Unknown; @@ -92,10 +92,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms else { orientation = (OrientationMode)Convert.ToUInt16(value.Value); - source.MetaData.ExifProfile.RemoveValue(ExifTag.Orientation); + source.Metadata.ExifProfile.RemoveValue(ExifTag.Orientation); } - source.MetaData.ExifProfile.SetValue(ExifTag.Orientation, (ushort)OrientationMode.TopLeft); + source.Metadata.ExifProfile.SetValue(ExifTag.Orientation, (ushort)OrientationMode.TopLeft); return orientation; } diff --git a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs index 3b1d7e94dd..5baa196a09 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs @@ -39,10 +39,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms protected override Image CreateDestination(Image source, Rectangle sourceRectangle) { // We will always be creating the clone even for mutate because we may need to resize the canvas - IEnumerable> frames = source.Frames.Select(x => new ImageFrame(source.GetConfiguration(), this.CropRectangle.Width, this.CropRectangle.Height, x.MetaData.DeepClone())); + IEnumerable> frames = source.Frames.Select(x => new ImageFrame(source.GetConfiguration(), this.CropRectangle.Width, this.CropRectangle.Height, x.Metadata.DeepClone())); // Use the overload to prevent an extra frame being added - return new Image(source.GetConfiguration(), source.MetaData.DeepClone(), frames); + return new Image(source.GetConfiguration(), source.Metadata.DeepClone(), frames); } /// diff --git a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs index 8a5d7563f4..6c7271c5ec 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs @@ -53,10 +53,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms { // We will always be creating the clone even for mutate because we may need to resize the canvas IEnumerable> frames = - source.Frames.Select(x => new ImageFrame(source.GetConfiguration(), this.TargetDimensions.Width, this.TargetDimensions.Height, x.MetaData.DeepClone())); + source.Frames.Select(x => new ImageFrame(source.GetConfiguration(), this.TargetDimensions.Width, this.TargetDimensions.Height, x.Metadata.DeepClone())); // Use the overload to prevent an extra frame being added - return new Image(source.GetConfiguration(), source.MetaData.DeepClone(), frames); + return new Image(source.GetConfiguration(), source.Metadata.DeepClone(), frames); } /// diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs index 2c186a276b..21011ac717 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs @@ -151,10 +151,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms protected override Image CreateDestination(Image source, Rectangle sourceRectangle) { // We will always be creating the clone even for mutate because we may need to resize the canvas - IEnumerable> frames = source.Frames.Select(x => new ImageFrame(source.GetConfiguration(), this.Width, this.Height, x.MetaData.DeepClone())); + IEnumerable> frames = source.Frames.Select(x => new ImageFrame(source.GetConfiguration(), this.Width, this.Height, x.Metadata.DeepClone())); // Use the overload to prevent an extra frame being added - return new Image(source.GetConfiguration(), source.MetaData.DeepClone(), frames); + return new Image(source.GetConfiguration(), source.Metadata.DeepClone(), frames); } /// diff --git a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs index 69ecf59215..57902a5e6d 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs @@ -5,7 +5,7 @@ using System; using System.Numerics; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -67,7 +67,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// protected override void AfterImageApply(Image source, Image destination, Rectangle sourceRectangle) { - ExifProfile profile = destination.MetaData.ExifProfile; + ExifProfile profile = destination.Metadata.ExifProfile; if (profile is null) { return; diff --git a/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorHelpers.cs b/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorHelpers.cs index f5536d0467..7bb666bce5 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorHelpers.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorHelpers.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Processing.Processors.Transforms @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public static void UpdateDimensionalMetData(Image image) where TPixel : struct, IPixel { - ExifProfile profile = image.MetaData.ExifProfile; + ExifProfile profile = image.Metadata.ExifProfile; if (profile is null) { return; diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index ad47ed1e7e..05fadd4584 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -13,7 +13,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests { - using SixLabors.ImageSharp.MetaData; + using SixLabors.ImageSharp.Metadata; using static TestImages.Bmp; public class BmpDecoderTests @@ -237,7 +237,7 @@ namespace SixLabors.ImageSharp.Tests var decoder = new BmpDecoder(); using (Image image = decoder.Decode(Configuration.Default, stream)) { - ImageMetaData meta = image.MetaData; + ImageMetadata meta = image.Metadata; Assert.Equal(xResolution, meta.HorizontalResolution); Assert.Equal(yResolution, meta.VerticalResolution); Assert.Equal(resolutionUnit, meta.ResolutionUnits); diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs index f818be8a91..c9745ef4f7 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs @@ -3,7 +3,7 @@ using System.IO; using SixLabors.ImageSharp.Formats.Bmp; -using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using Xunit; @@ -55,7 +55,7 @@ namespace SixLabors.ImageSharp.Tests memStream.Position = 0; using (var output = Image.Load(memStream)) { - ImageMetaData meta = output.MetaData; + ImageMetadata meta = output.Metadata; Assert.Equal(xResolution, meta.HorizontalResolution); Assert.Equal(yResolution, meta.VerticalResolution); Assert.Equal(resolutionUnit, meta.ResolutionUnits); @@ -80,7 +80,7 @@ namespace SixLabors.ImageSharp.Tests memStream.Position = 0; using (var output = Image.Load(memStream)) { - BmpMetaData meta = output.MetaData.GetFormatMetaData(BmpFormat.Instance); + BmpMetadata meta = output.Metadata.GetFormatMetadata(BmpFormat.Instance); Assert.Equal(bmpBitsPerPixel, meta.BitsPerPixel); } diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpMetaDataTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpMetaDataTests.cs index 991768a11a..ab72214f6c 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpMetaDataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpMetaDataTests.cs @@ -11,8 +11,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp [Fact] public void CloneIsDeep() { - var meta = new BmpMetaData() { BitsPerPixel = BmpBitsPerPixel.Pixel24 }; - var clone = (BmpMetaData)meta.DeepClone(); + var meta = new BmpMetadata() { BitsPerPixel = BmpBitsPerPixel.Pixel24 }; + var clone = (BmpMetadata)meta.DeepClone(); clone.BitsPerPixel = BmpBitsPerPixel.Pixel32; diff --git a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs index aed1edfbf0..e8fdbd8926 100644 --- a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs +++ b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs @@ -27,8 +27,8 @@ namespace SixLabors.ImageSharp.Tests { using (Image image = provider.GetImage()) { - image.MetaData.VerticalResolution = 150; - image.MetaData.HorizontalResolution = 150; + image.Metadata.VerticalResolution = 150; + image.Metadata.HorizontalResolution = 150; image.DebugSave(provider); } } diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs index 5bfbb058be..b13362aa3b 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs @@ -7,7 +7,7 @@ using System.IO; using System.Text; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats.Gif; -using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using Xunit; @@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif public static readonly TheoryData RatioFiles = new TheoryData { - { TestImages.Gif.Rings, (int)ImageMetaData.DefaultHorizontalResolution, (int)ImageMetaData.DefaultVerticalResolution , PixelResolutionUnit.PixelsPerInch}, + { TestImages.Gif.Rings, (int)ImageMetadata.DefaultHorizontalResolution, (int)ImageMetadata.DefaultVerticalResolution , PixelResolutionUnit.PixelsPerInch}, { TestImages.Gif.Ratio1x4, 1, 4 , PixelResolutionUnit.AspectRatio}, { TestImages.Gif.Ratio4x1, 4, 1, PixelResolutionUnit.AspectRatio } }; @@ -101,7 +101,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif var decoder = new GifDecoder(); using (Image image = decoder.Decode(Configuration.Default, stream)) { - ImageMetaData meta = image.MetaData; + ImageMetadata meta = image.Metadata; Assert.Equal(xResolution, meta.HorizontalResolution); Assert.Equal(yResolution, meta.VerticalResolution); Assert.Equal(resolutionUnit, meta.ResolutionUnits); @@ -118,7 +118,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif { var decoder = new GifDecoder(); IImageInfo image = decoder.Identify(Configuration.Default, stream); - ImageMetaData meta = image.MetaData; + ImageMetadata meta = image.Metadata; Assert.Equal(xResolution, meta.HorizontalResolution); Assert.Equal(yResolution, meta.VerticalResolution); Assert.Equal(resolutionUnit, meta.ResolutionUnits); @@ -167,9 +167,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif using (Image image = testFile.CreateImage(options)) { - Assert.Equal(1, image.MetaData.Properties.Count); - Assert.Equal("Comments", image.MetaData.Properties[0].Name); - Assert.Equal("ImageSharp", image.MetaData.Properties[0].Value); + Assert.Equal(1, image.Metadata.Properties.Count); + Assert.Equal("Comments", image.Metadata.Properties[0].Name); + Assert.Equal("ImageSharp", image.Metadata.Properties[0].Value); } } @@ -185,7 +185,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif using (Image image = testFile.CreateImage(options)) { - Assert.Equal(0, image.MetaData.Properties.Count); + Assert.Equal(0, image.Metadata.Properties.Count); } } @@ -201,8 +201,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif using (Image image = testFile.CreateImage(options)) { - Assert.Equal(1, image.MetaData.Properties.Count); - Assert.Equal("浉条卥慨灲", image.MetaData.Properties[0].Value); + Assert.Equal(1, image.Metadata.Properties.Count); + Assert.Equal("浉条卥慨灲", image.Metadata.Properties[0].Value); } } diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs index b74a7a85f9..cac4030d5f 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs @@ -3,7 +3,7 @@ using System.IO; using SixLabors.ImageSharp.Formats.Gif; -using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Quantization; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; @@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif public static readonly TheoryData RatioFiles = new TheoryData { - { TestImages.Gif.Rings, (int)ImageMetaData.DefaultHorizontalResolution, (int)ImageMetaData.DefaultVerticalResolution , PixelResolutionUnit.PixelsPerInch}, + { TestImages.Gif.Rings, (int)ImageMetadata.DefaultHorizontalResolution, (int)ImageMetadata.DefaultVerticalResolution , PixelResolutionUnit.PixelsPerInch}, { TestImages.Gif.Ratio1x4, 1, 4 , PixelResolutionUnit.AspectRatio}, { TestImages.Gif.Ratio4x1, 4, 1, PixelResolutionUnit.AspectRatio } }; @@ -67,7 +67,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif memStream.Position = 0; using (var output = Image.Load(memStream)) { - ImageMetaData meta = output.MetaData; + ImageMetadata meta = output.Metadata; Assert.Equal(xResolution, meta.HorizontalResolution); Assert.Equal(yResolution, meta.VerticalResolution); Assert.Equal(resolutionUnit, meta.ResolutionUnits); @@ -92,9 +92,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif memStream.Position = 0; using (var output = Image.Load(memStream)) { - Assert.Equal(1, output.MetaData.Properties.Count); - Assert.Equal("Comments", output.MetaData.Properties[0].Name); - Assert.Equal("ImageSharp", output.MetaData.Properties[0].Value); + Assert.Equal(1, output.Metadata.Properties.Count); + Assert.Equal("Comments", output.Metadata.Properties[0].Name); + Assert.Equal("ImageSharp", output.Metadata.Properties[0].Value); } } } @@ -109,7 +109,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif using (Image input = testFile.CreateImage()) { - input.MetaData.Properties.Clear(); + input.Metadata.Properties.Clear(); using (var memStream = new MemoryStream()) { input.SaveAsGif(memStream, options); @@ -117,7 +117,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif memStream.Position = 0; using (var output = Image.Load(memStream)) { - Assert.Equal(0, output.MetaData.Properties.Count); + Assert.Equal(0, output.Metadata.Properties.Count); } } } @@ -129,7 +129,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif using (var input = new Image(1, 1)) { string comments = new string('c', 256); - input.MetaData.Properties.Add(new ImageProperty("Comments", comments)); + input.Metadata.Properties.Add(new ImageProperty("Comments", comments)); using (var memStream = new MemoryStream()) { @@ -138,9 +138,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif memStream.Position = 0; using (var output = Image.Load(memStream)) { - Assert.Equal(1, output.MetaData.Properties.Count); - Assert.Equal("Comments", output.MetaData.Properties[0].Name); - Assert.Equal(255, output.MetaData.Properties[0].Value.Length); + Assert.Equal(1, output.Metadata.Properties.Count); + Assert.Equal("Comments", output.Metadata.Properties[0].Name); + Assert.Equal(255, output.Metadata.Properties[0].Value.Length); } } } @@ -181,8 +181,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif inStream.Position = 0; var image = Image.Load(inStream); - GifMetaData metaData = image.MetaData.GetFormatMetaData(GifFormat.Instance); - GifFrameMetaData frameMetaData = image.Frames.RootFrame.MetaData.GetFormatMetaData(GifFormat.Instance); + GifMetadata metaData = image.Metadata.GetFormatMetadata(GifFormat.Instance); + GifFrameMetadata frameMetaData = image.Frames.RootFrame.Metadata.GetFormatMetadata(GifFormat.Instance); GifColorTableMode colorMode = metaData.ColorTableMode; var encoder = new GifEncoder() { @@ -196,7 +196,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif outStream.Position = 0; var clone = Image.Load(outStream); - GifMetaData cloneMetaData = clone.MetaData.GetFormatMetaData(GifFormat.Instance); + GifMetadata cloneMetaData = clone.Metadata.GetFormatMetadata(GifFormat.Instance); Assert.Equal(metaData.ColorTableMode, cloneMetaData.ColorTableMode); // Gifiddle and Cyotek GifInfo say this image has 64 colors. @@ -204,8 +204,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif for (int i = 0; i < image.Frames.Count; i++) { - GifFrameMetaData ifm = image.Frames[i].MetaData.GetFormatMetaData(GifFormat.Instance); - GifFrameMetaData cifm = clone.Frames[i].MetaData.GetFormatMetaData(GifFormat.Instance); + GifFrameMetadata ifm = image.Frames[i].Metadata.GetFormatMetadata(GifFormat.Instance); + GifFrameMetadata cifm = clone.Frames[i].Metadata.GetFormatMetadata(GifFormat.Instance); Assert.Equal(ifm.ColorTableLength, cifm.ColorTableLength); Assert.Equal(ifm.FrameDelay, cifm.FrameDelay); diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifFrameMetaDataTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifFrameMetaDataTests.cs index a39fc47b40..d82bfbf227 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifFrameMetaDataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifFrameMetaDataTests.cs @@ -11,14 +11,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif [Fact] public void CloneIsDeep() { - var meta = new GifFrameMetaData() + var meta = new GifFrameMetadata() { FrameDelay = 1, DisposalMethod = GifDisposalMethod.RestoreToBackground, ColorTableLength = 2 }; - var clone = (GifFrameMetaData)meta.DeepClone(); + var clone = (GifFrameMetadata)meta.DeepClone(); clone.FrameDelay = 2; clone.DisposalMethod = GifDisposalMethod.RestoreToPrevious; diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifMetaDataTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifMetaDataTests.cs index 29db32b4ab..8510a3461c 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifMetaDataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifMetaDataTests.cs @@ -11,14 +11,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif [Fact] public void CloneIsDeep() { - var meta = new GifMetaData() + var meta = new GifMetadata() { RepeatCount = 1, ColorTableMode = GifColorTableMode.Global, GlobalColorTableLength = 2 }; - var clone = (GifMetaData)meta.DeepClone(); + var clone = (GifMetadata)meta.DeepClone(); clone.RepeatCount = 2; clone.ColorTableMode = GifColorTableMode.Local; diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JFifMarkerTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JFifMarkerTests.cs index b2dc3534d1..aebf80d082 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JFifMarkerTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JFifMarkerTests.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; -using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.Metadata; using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Jpg diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.MetaData.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.MetaData.cs index 1f1583a39f..48acc9ea47 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.MetaData.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.MetaData.cs @@ -3,8 +3,8 @@ using System.IO; using SixLabors.ImageSharp.Formats; -using SixLabors.ImageSharp.MetaData.Profiles.Exif; -using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using SixLabors.ImageSharp.Metadata.Profiles.Exif; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; using SixLabors.ImageSharp.PixelFormats; using Xunit; @@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Formats.Jpeg; - using SixLabors.ImageSharp.MetaData; + using SixLabors.ImageSharp.Metadata; public partial class JpegDecoderTests { @@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg var decoder = new JpegDecoder(); using (Image image = decoder.Decode(Configuration.Default, stream)) { - ImageMetaData meta = image.MetaData; + ImageMetadata meta = image.Metadata; Assert.Equal(xResolution, meta.HorizontalResolution); Assert.Equal(yResolution, meta.VerticalResolution); Assert.Equal(resolutionUnit, meta.ResolutionUnits); @@ -102,7 +102,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg { var decoder = new JpegDecoder(); IImageInfo image = decoder.Identify(Configuration.Default, stream); - ImageMetaData meta = image.MetaData; + ImageMetadata meta = image.Metadata; Assert.Equal(xResolution, meta.HorizontalResolution); Assert.Equal(yResolution, meta.VerticalResolution); Assert.Equal(resolutionUnit, meta.ResolutionUnits); @@ -118,7 +118,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg { var decoder = new JpegDecoder(); IImageInfo image = decoder.Identify(Configuration.Default, stream); - JpegMetaData meta = image.MetaData.GetFormatMetaData(JpegFormat.Instance); + JpegMetadata meta = image.Metadata.GetFormatMetadata(JpegFormat.Instance); Assert.Equal(quality, meta.Quality); } } @@ -133,7 +133,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg var decoder = new JpegDecoder(); using (Image image = decoder.Decode(Configuration.Default, stream)) { - JpegMetaData meta = image.MetaData.GetFormatMetaData(JpegFormat.Instance); + JpegMetadata meta = image.Metadata.GetFormatMetadata(JpegFormat.Instance); Assert.Equal(quality, meta.Quality); } } @@ -180,7 +180,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg Assert.Equal(bpp32, imageInfo.PixelType.BitsPerPixel); } - ExifProfile exifProfile = imageInfo.MetaData.ExifProfile; + ExifProfile exifProfile = imageInfo.Metadata.ExifProfile; if (exifProfilePresent) { @@ -192,7 +192,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg Assert.Null(exifProfile); } - IccProfile iccProfile = imageInfo.MetaData.IccProfile; + IccProfile iccProfile = imageInfo.Metadata.IccProfile; if (iccProfilePresent) { @@ -220,13 +220,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg { if (ignoreMetaData) { - Assert.Null(image.MetaData.ExifProfile); - Assert.Null(image.MetaData.IccProfile); + Assert.Null(image.Metadata.ExifProfile); + Assert.Null(image.Metadata.IccProfile); } else { - Assert.NotNull(image.MetaData.ExifProfile); - Assert.NotNull(image.MetaData.IccProfile); + Assert.NotNull(image.Metadata.ExifProfile); + Assert.NotNull(image.Metadata.IccProfile); } } } @@ -239,8 +239,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg TestImageInfo(TestImages.Jpeg.Baseline.Floorplan, JpegDecoder, useIdentify, imageInfo => { - Assert.Equal(300, imageInfo.MetaData.HorizontalResolution); - Assert.Equal(300, imageInfo.MetaData.VerticalResolution); + Assert.Equal(300, imageInfo.Metadata.HorizontalResolution); + Assert.Equal(300, imageInfo.Metadata.VerticalResolution); }); } @@ -252,8 +252,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg TestImageInfo(TestImages.Jpeg.Baseline.Jpeg420Exif, JpegDecoder, useIdentify, imageInfo => { - Assert.Equal(72, imageInfo.MetaData.HorizontalResolution); - Assert.Equal(72, imageInfo.MetaData.VerticalResolution); + Assert.Equal(72, imageInfo.Metadata.HorizontalResolution); + Assert.Equal(72, imageInfo.Metadata.VerticalResolution); }); } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs index 598d99274a..618947130a 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs @@ -3,7 +3,7 @@ using System.IO; using SixLabors.ImageSharp.Formats.Jpeg; -using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; @@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg memStream.Position = 0; using (var output = Image.Load(memStream)) { - JpegMetaData meta = output.MetaData.GetFormatMetaData(JpegFormat.Instance); + JpegMetadata meta = output.Metadata.GetFormatMetadata(JpegFormat.Instance); Assert.Equal(quality, meta.Quality); } } @@ -189,7 +189,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg memStream.Position = 0; using (var output = Image.Load(memStream)) { - ImageMetaData meta = output.MetaData; + ImageMetadata meta = output.Metadata; Assert.Equal(xResolution, meta.HorizontalResolution); Assert.Equal(yResolution, meta.VerticalResolution); Assert.Equal(resolutionUnit, meta.ResolutionUnits); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegMetaDataTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegMetaDataTests.cs index 431de4be31..793bdd5229 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegMetaDataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegMetaDataTests.cs @@ -11,8 +11,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [Fact] public void CloneIsDeep() { - var meta = new JpegMetaData() { Quality = 50 }; - var clone = (JpegMetaData)meta.DeepClone(); + var meta = new JpegMetadata() { Quality = 50 }; + var clone = (JpegMetadata)meta.DeepClone(); clone.Quality = 99; diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index fe406c9924..0dbccd2509 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -8,7 +8,7 @@ using System.IO; using System.Text; using SixLabors.ImageSharp.Formats.Png; -using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; @@ -204,9 +204,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png using (Image image = testFile.CreateImage(options)) { - Assert.Equal(1, image.MetaData.Properties.Count); - Assert.Equal("Software", image.MetaData.Properties[0].Name); - Assert.Equal("paint.net 4.0.6", image.MetaData.Properties[0].Value); + Assert.Equal(1, image.Metadata.Properties.Count); + Assert.Equal("Software", image.Metadata.Properties[0].Name); + Assert.Equal("paint.net 4.0.6", image.Metadata.Properties[0].Value); } } @@ -222,7 +222,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png using (Image image = testFile.CreateImage(options)) { - Assert.Equal(0, image.MetaData.Properties.Count); + Assert.Equal(0, image.Metadata.Properties.Count); } } @@ -238,8 +238,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png using (Image image = testFile.CreateImage(options)) { - Assert.Equal(1, image.MetaData.Properties.Count); - Assert.Equal("潓瑦慷敲", image.MetaData.Properties[0].Name); + Assert.Equal(1, image.Metadata.Properties.Count); + Assert.Equal("潓瑦慷敲", image.Metadata.Properties[0].Name); } } @@ -270,7 +270,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png var decoder = new PngDecoder(); using (Image image = decoder.Decode(Configuration.Default, stream)) { - ImageMetaData meta = image.MetaData; + ImageMetadata meta = image.Metadata; Assert.Equal(xResolution, meta.HorizontalResolution); Assert.Equal(yResolution, meta.VerticalResolution); Assert.Equal(resolutionUnit, meta.ResolutionUnits); @@ -287,7 +287,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png { var decoder = new PngDecoder(); IImageInfo image = decoder.Identify(Configuration.Default, stream); - ImageMetaData meta = image.MetaData; + ImageMetadata meta = image.Metadata; Assert.Equal(xResolution, meta.HorizontalResolution); Assert.Equal(yResolution, meta.VerticalResolution); Assert.Equal(resolutionUnit, meta.ResolutionUnits); diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index 9079b15fb0..5aa69dd6af 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -7,7 +7,7 @@ using System.Linq; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Png; -using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Quantization; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; @@ -228,7 +228,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png memStream.Position = 0; using (var output = Image.Load(memStream)) { - ImageMetaData meta = output.MetaData; + ImageMetadata meta = output.Metadata; Assert.Equal(xResolution, meta.HorizontalResolution); Assert.Equal(yResolution, meta.VerticalResolution); Assert.Equal(resolutionUnit, meta.ResolutionUnits); @@ -253,7 +253,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png memStream.Position = 0; using (var output = Image.Load(memStream)) { - PngMetaData meta = output.MetaData.GetFormatMetaData(PngFormat.Instance); + PngMetadata meta = output.Metadata.GetFormatMetadata(PngFormat.Instance); Assert.Equal(pngBitDepth, meta.BitDepth); } @@ -270,7 +270,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png var testFile = TestFile.Create(imagePath); using (Image input = testFile.CreateImage()) { - PngMetaData inMeta = input.MetaData.GetFormatMetaData(PngFormat.Instance); + PngMetadata inMeta = input.Metadata.GetFormatMetadata(PngFormat.Instance); Assert.True(inMeta.HasTrans); using (var memStream = new MemoryStream()) @@ -279,7 +279,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png memStream.Position = 0; using (var output = Image.Load(memStream)) { - PngMetaData outMeta = output.MetaData.GetFormatMetaData(PngFormat.Instance); + PngMetadata outMeta = output.Metadata.GetFormatMetadata(PngFormat.Instance); Assert.True(outMeta.HasTrans); switch (pngColorType) diff --git a/tests/ImageSharp.Tests/Formats/Png/PngMetaDataTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngMetaDataTests.cs index a21bb9acbe..72fc2f8656 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngMetaDataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngMetaDataTests.cs @@ -11,13 +11,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png [Fact] public void CloneIsDeep() { - var meta = new PngMetaData() + var meta = new PngMetadata() { BitDepth = PngBitDepth.Bit16, ColorType = PngColorType.GrayscaleWithAlpha, Gamma = 2 }; - var clone = (PngMetaData)meta.DeepClone(); + var clone = (PngMetadata)meta.DeepClone(); clone.BitDepth = PngBitDepth.Bit2; clone.ColorType = PngColorType.Palette; diff --git a/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs b/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs index 16d999ad25..d51470292d 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs @@ -9,7 +9,7 @@ using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Common.Helpers; -using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Shapes; using SixLabors.ImageSharp.Processing; @@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp.Tests public void WrapMemory_CreatedImageIsCorrect() { Configuration cfg = Configuration.Default.Clone(); - var metaData = new ImageMetaData(); + var metaData = new ImageMetadata(); var array = new Rgba32[25]; var memory = new Memory(array); @@ -96,7 +96,7 @@ namespace SixLabors.ImageSharp.Tests Assert.True(Unsafe.AreSame(ref array[0], ref pixel0)); Assert.Equal(cfg, image.GetConfiguration()); - Assert.Equal(metaData, image.MetaData); + Assert.Equal(metaData, image.Metadata); } } diff --git a/tests/ImageSharp.Tests/Image/ImageTests.cs b/tests/ImageSharp.Tests/Image/ImageTests.cs index c5c7d19e1e..60384c0578 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.Memory; @@ -72,14 +72,14 @@ namespace SixLabors.ImageSharp.Tests byte dirtyValue = 123; configuration.MemoryAllocator = new TestMemoryAllocator(dirtyValue); - var metadata = new ImageMetaData(); + var metadata = new ImageMetadata(); using (Image image = Image.CreateUninitialized(configuration, 21, 22, metadata)) { Assert.Equal(21, image.Width); Assert.Equal(22, image.Height); Assert.Same(configuration, image.GetConfiguration()); - Assert.Same(metadata, image.MetaData); + Assert.Same(metadata, image.Metadata); Assert.Equal(dirtyValue, image[5, 5].PackedValue); } diff --git a/tests/ImageSharp.Tests/ImageInfoTests.cs b/tests/ImageSharp.Tests/ImageInfoTests.cs index 91f6804c0f..67804a18fd 100644 --- a/tests/ImageSharp.Tests/ImageInfoTests.cs +++ b/tests/ImageSharp.Tests/ImageInfoTests.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Formats; -using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.Metadata; using SixLabors.Primitives; using Xunit; @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Tests var size = new Size(Width, Height); var rectangle = new Rectangle(0, 0, Width, Height); var pixelType = new PixelTypeInfo(8); - var meta = new ImageMetaData(); + var meta = new ImageMetadata(); var info = new ImageInfo(pixelType, Width, Height, meta); @@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Tests Assert.Equal(Height, info.Height); Assert.Equal(size, info.Size()); Assert.Equal(rectangle, info.Bounds()); - Assert.Equal(meta, info.MetaData); + Assert.Equal(meta, info.Metadata); } } } diff --git a/tests/ImageSharp.Tests/MetaData/ImageFrameMetaDataTests.cs b/tests/ImageSharp.Tests/MetaData/ImageFrameMetaDataTests.cs index 8c49039603..bafb117f75 100644 --- a/tests/ImageSharp.Tests/MetaData/ImageFrameMetaDataTests.cs +++ b/tests/ImageSharp.Tests/MetaData/ImageFrameMetaDataTests.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Formats.Gif; -using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.Metadata; using Xunit; namespace SixLabors.ImageSharp.Tests @@ -19,14 +19,14 @@ namespace SixLabors.ImageSharp.Tests const int colorTableLength = 128; const GifDisposalMethod disposalMethod = GifDisposalMethod.RestoreToBackground; - var metaData = new ImageFrameMetaData(); - GifFrameMetaData gifFrameMetaData = metaData.GetFormatMetaData(GifFormat.Instance); + var metaData = new ImageFrameMetadata(); + GifFrameMetadata gifFrameMetaData = metaData.GetFormatMetadata(GifFormat.Instance); gifFrameMetaData.FrameDelay = frameDelay; gifFrameMetaData.ColorTableLength = colorTableLength; gifFrameMetaData.DisposalMethod = disposalMethod; - var clone = new ImageFrameMetaData(metaData); - GifFrameMetaData cloneGifFrameMetaData = clone.GetFormatMetaData(GifFormat.Instance); + var clone = new ImageFrameMetadata(metaData); + GifFrameMetadata cloneGifFrameMetaData = clone.GetFormatMetadata(GifFormat.Instance); Assert.Equal(frameDelay, cloneGifFrameMetaData.FrameDelay); Assert.Equal(colorTableLength, cloneGifFrameMetaData.ColorTableLength); @@ -36,9 +36,9 @@ namespace SixLabors.ImageSharp.Tests [Fact] public void CloneIsDeep() { - var metaData = new ImageFrameMetaData(); - ImageFrameMetaData clone = metaData.DeepClone(); - Assert.False(metaData.GetFormatMetaData(GifFormat.Instance).Equals(clone.GetFormatMetaData(GifFormat.Instance))); + var metaData = new ImageFrameMetadata(); + ImageFrameMetadata clone = metaData.DeepClone(); + Assert.False(metaData.GetFormatMetadata(GifFormat.Instance).Equals(clone.GetFormatMetadata(GifFormat.Instance))); } } } diff --git a/tests/ImageSharp.Tests/MetaData/ImageMetaDataTests.cs b/tests/ImageSharp.Tests/MetaData/ImageMetaDataTests.cs index b9619cb3f8..5f02ce7aeb 100644 --- a/tests/ImageSharp.Tests/MetaData/ImageMetaDataTests.cs +++ b/tests/ImageSharp.Tests/MetaData/ImageMetaDataTests.cs @@ -2,8 +2,8 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Formats.Gif; -using SixLabors.ImageSharp.MetaData; -using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using SixLabors.ImageSharp.Metadata; +using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; @@ -12,14 +12,14 @@ using Xunit; namespace SixLabors.ImageSharp.Tests { /// - /// Tests the class. + /// Tests the class. /// public class ImageMetaDataTests { [Fact] public void ConstructorImageMetaData() { - var metaData = new ImageMetaData(); + var metaData = new ImageMetadata(); var exifProfile = new ExifProfile(); var imageProperty = new ImageProperty("name", "value"); @@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Tests metaData.VerticalResolution = 2; metaData.Properties.Add(imageProperty); - ImageMetaData clone = metaData.DeepClone(); + ImageMetadata clone = metaData.DeepClone(); Assert.Equal(exifProfile.ToByteArray(), clone.ExifProfile.ToByteArray()); Assert.Equal(4, clone.HorizontalResolution); @@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Tests [Fact] public void CloneIsDeep() { - var metaData = new ImageMetaData(); + var metaData = new ImageMetadata(); var exifProfile = new ExifProfile(); var imageProperty = new ImageProperty("name", "value"); @@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.Tests metaData.VerticalResolution = 2; metaData.Properties.Add(imageProperty); - ImageMetaData clone = metaData.DeepClone(); + ImageMetadata clone = metaData.DeepClone(); clone.HorizontalResolution = 2; clone.VerticalResolution = 4; @@ -58,13 +58,13 @@ namespace SixLabors.ImageSharp.Tests Assert.False(metaData.HorizontalResolution.Equals(clone.HorizontalResolution)); Assert.False(metaData.VerticalResolution.Equals(clone.VerticalResolution)); Assert.False(metaData.Properties.Equals(clone.Properties)); - Assert.False(metaData.GetFormatMetaData(GifFormat.Instance).Equals(clone.GetFormatMetaData(GifFormat.Instance))); + Assert.False(metaData.GetFormatMetadata(GifFormat.Instance).Equals(clone.GetFormatMetadata(GifFormat.Instance))); } [Fact] public void HorizontalResolution() { - var metaData = new ImageMetaData(); + var metaData = new ImageMetadata(); Assert.Equal(96, metaData.HorizontalResolution); metaData.HorizontalResolution = 0; @@ -80,7 +80,7 @@ namespace SixLabors.ImageSharp.Tests [Fact] public void VerticalResolution() { - var metaData = new ImageMetaData(); + var metaData = new ImageMetadata(); Assert.Equal(96, metaData.VerticalResolution); metaData.VerticalResolution = 0; @@ -101,14 +101,14 @@ namespace SixLabors.ImageSharp.Tests exifProfile.SetValue(ExifTag.YResolution, new Rational(300)); var image = new Image(1, 1); - image.MetaData.ExifProfile = exifProfile; - image.MetaData.HorizontalResolution = 400; - image.MetaData.VerticalResolution = 500; + image.Metadata.ExifProfile = exifProfile; + image.Metadata.HorizontalResolution = 400; + image.Metadata.VerticalResolution = 500; - image.MetaData.SyncProfiles(); + image.Metadata.SyncProfiles(); - Assert.Equal(400, ((Rational)image.MetaData.ExifProfile.GetValue(ExifTag.XResolution).Value).ToDouble()); - Assert.Equal(500, ((Rational)image.MetaData.ExifProfile.GetValue(ExifTag.YResolution).Value).ToDouble()); + Assert.Equal(400, ((Rational)image.Metadata.ExifProfile.GetValue(ExifTag.XResolution).Value).ToDouble()); + Assert.Equal(500, ((Rational)image.Metadata.ExifProfile.GetValue(ExifTag.YResolution).Value).ToDouble()); } } } diff --git a/tests/ImageSharp.Tests/MetaData/ImagePropertyTests.cs b/tests/ImageSharp.Tests/MetaData/ImagePropertyTests.cs index b5886522a8..8cce5ba414 100644 --- a/tests/ImageSharp.Tests/MetaData/ImagePropertyTests.cs +++ b/tests/ImageSharp.Tests/MetaData/ImagePropertyTests.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; -using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.Metadata; using Xunit; namespace SixLabors.ImageSharp.Tests diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs index c10ffb6c8e..9d145f3805 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs @@ -8,8 +8,8 @@ using System.IO; using System.Linq; using System.Text; using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; -using SixLabors.ImageSharp.MetaData; -using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using SixLabors.ImageSharp.Metadata; +using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; @@ -43,17 +43,17 @@ namespace SixLabors.ImageSharp.Tests { Image image = TestFile.Create(TestImages.Jpeg.Baseline.Calliphora).CreateImage(); - Assert.Null(image.MetaData.ExifProfile); + Assert.Null(image.Metadata.ExifProfile); - image.MetaData.ExifProfile = new ExifProfile(); - image.MetaData.ExifProfile.SetValue(ExifTag.Copyright, "Dirk Lemstra"); + image.Metadata.ExifProfile = new ExifProfile(); + image.Metadata.ExifProfile.SetValue(ExifTag.Copyright, "Dirk Lemstra"); image = WriteAndRead(image, imageFormat); - Assert.NotNull(image.MetaData.ExifProfile); - Assert.Equal(1, image.MetaData.ExifProfile.Values.Count()); + Assert.NotNull(image.Metadata.ExifProfile); + Assert.Equal(1, image.Metadata.ExifProfile.Values.Count()); - ExifValue value = image.MetaData.ExifProfile.Values.FirstOrDefault(val => val.Tag == ExifTag.Copyright); + ExifValue value = image.Metadata.ExifProfile.Values.FirstOrDefault(val => val.Tag == ExifTag.Copyright); TestValue(value, "Dirk Lemstra"); } @@ -94,11 +94,11 @@ namespace SixLabors.ImageSharp.Tests profile.SetValue(ExifTag.ExposureTime, new Rational(exposureTime)); var image = new Image(1, 1); - image.MetaData.ExifProfile = profile; + image.Metadata.ExifProfile = profile; image = WriteAndRead(image, imageFormat); - profile = image.MetaData.ExifProfile; + profile = image.Metadata.ExifProfile; Assert.NotNull(profile); ExifValue value = profile.GetValue(ExifTag.ExposureTime); @@ -109,11 +109,11 @@ namespace SixLabors.ImageSharp.Tests profile = GetExifProfile(); profile.SetValue(ExifTag.ExposureTime, new Rational(exposureTime, true)); - image.MetaData.ExifProfile = profile; + image.Metadata.ExifProfile = profile; image = WriteAndRead(image, imageFormat); - profile = image.MetaData.ExifProfile; + profile = image.Metadata.ExifProfile; Assert.NotNull(profile); value = profile.GetValue(ExifTag.ExposureTime); @@ -127,24 +127,24 @@ namespace SixLabors.ImageSharp.Tests public void ReadWriteInfinity(TestImageWriteFormat imageFormat) { Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateImage(); - image.MetaData.ExifProfile.SetValue(ExifTag.ExposureBiasValue, new SignedRational(double.PositiveInfinity)); + image.Metadata.ExifProfile.SetValue(ExifTag.ExposureBiasValue, new SignedRational(double.PositiveInfinity)); image = WriteAndReadJpeg(image); - ExifValue value = image.MetaData.ExifProfile.GetValue(ExifTag.ExposureBiasValue); + ExifValue value = image.Metadata.ExifProfile.GetValue(ExifTag.ExposureBiasValue); Assert.NotNull(value); Assert.Equal(new SignedRational(double.PositiveInfinity), value.Value); - image.MetaData.ExifProfile.SetValue(ExifTag.ExposureBiasValue, new SignedRational(double.NegativeInfinity)); + image.Metadata.ExifProfile.SetValue(ExifTag.ExposureBiasValue, new SignedRational(double.NegativeInfinity)); image = WriteAndRead(image, imageFormat); - value = image.MetaData.ExifProfile.GetValue(ExifTag.ExposureBiasValue); + value = image.Metadata.ExifProfile.GetValue(ExifTag.ExposureBiasValue); Assert.NotNull(value); Assert.Equal(new SignedRational(double.NegativeInfinity), value.Value); - image.MetaData.ExifProfile.SetValue(ExifTag.FlashEnergy, new Rational(double.NegativeInfinity)); + image.Metadata.ExifProfile.SetValue(ExifTag.FlashEnergy, new Rational(double.NegativeInfinity)); image = WriteAndRead(image, imageFormat); - value = image.MetaData.ExifProfile.GetValue(ExifTag.FlashEnergy); + value = image.Metadata.ExifProfile.GetValue(ExifTag.FlashEnergy); Assert.NotNull(value); Assert.Equal(new Rational(double.PositiveInfinity), value.Value); } @@ -157,74 +157,74 @@ namespace SixLabors.ImageSharp.Tests var latitude = new Rational[] { new Rational(12.3), new Rational(4.56), new Rational(789.0) }; Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateImage(); - image.MetaData.ExifProfile.SetValue(ExifTag.Software, "ImageSharp"); + image.Metadata.ExifProfile.SetValue(ExifTag.Software, "ImageSharp"); - ExifValue value = image.MetaData.ExifProfile.GetValue(ExifTag.Software); + ExifValue value = image.Metadata.ExifProfile.GetValue(ExifTag.Software); TestValue(value, "ImageSharp"); Assert.Throws(() => { value.WithValue(15); }); - image.MetaData.ExifProfile.SetValue(ExifTag.ShutterSpeedValue, new SignedRational(75.55)); + image.Metadata.ExifProfile.SetValue(ExifTag.ShutterSpeedValue, new SignedRational(75.55)); - value = image.MetaData.ExifProfile.GetValue(ExifTag.ShutterSpeedValue); + value = image.Metadata.ExifProfile.GetValue(ExifTag.ShutterSpeedValue); TestValue(value, new SignedRational(7555, 100)); Assert.Throws(() => { value.WithValue(75); }); - image.MetaData.ExifProfile.SetValue(ExifTag.XResolution, new Rational(150.0)); + image.Metadata.ExifProfile.SetValue(ExifTag.XResolution, new Rational(150.0)); // We also need to change this value because this overrides XResolution when the image is written. - image.MetaData.HorizontalResolution = 150.0; + image.Metadata.HorizontalResolution = 150.0; - value = image.MetaData.ExifProfile.GetValue(ExifTag.XResolution); + value = image.Metadata.ExifProfile.GetValue(ExifTag.XResolution); TestValue(value, new Rational(150, 1)); Assert.Throws(() => { value.WithValue("ImageSharp"); }); - image.MetaData.ExifProfile.SetValue(ExifTag.ReferenceBlackWhite, null); + image.Metadata.ExifProfile.SetValue(ExifTag.ReferenceBlackWhite, null); - value = image.MetaData.ExifProfile.GetValue(ExifTag.ReferenceBlackWhite); + value = image.Metadata.ExifProfile.GetValue(ExifTag.ReferenceBlackWhite); TestValue(value, (string)null); - image.MetaData.ExifProfile.SetValue(ExifTag.GPSLatitude, latitude); + image.Metadata.ExifProfile.SetValue(ExifTag.GPSLatitude, latitude); - value = image.MetaData.ExifProfile.GetValue(ExifTag.GPSLatitude); + value = image.Metadata.ExifProfile.GetValue(ExifTag.GPSLatitude); TestValue(value, latitude); image = WriteAndRead(image, imageFormat); - Assert.NotNull(image.MetaData.ExifProfile); - Assert.Equal(17, image.MetaData.ExifProfile.Values.Count()); + Assert.NotNull(image.Metadata.ExifProfile); + Assert.Equal(17, image.Metadata.ExifProfile.Values.Count()); - value = image.MetaData.ExifProfile.GetValue(ExifTag.Software); + value = image.Metadata.ExifProfile.GetValue(ExifTag.Software); TestValue(value, "ImageSharp"); - value = image.MetaData.ExifProfile.GetValue(ExifTag.ShutterSpeedValue); + value = image.Metadata.ExifProfile.GetValue(ExifTag.ShutterSpeedValue); TestValue(value, new SignedRational(75.55)); - value = image.MetaData.ExifProfile.GetValue(ExifTag.XResolution); + value = image.Metadata.ExifProfile.GetValue(ExifTag.XResolution); TestValue(value, new Rational(150.0)); - value = image.MetaData.ExifProfile.GetValue(ExifTag.ReferenceBlackWhite); + value = image.Metadata.ExifProfile.GetValue(ExifTag.ReferenceBlackWhite); Assert.Null(value); - value = image.MetaData.ExifProfile.GetValue(ExifTag.GPSLatitude); + value = image.Metadata.ExifProfile.GetValue(ExifTag.GPSLatitude); TestValue(value, latitude); - image.MetaData.ExifProfile.Parts = ExifParts.ExifTags; + image.Metadata.ExifProfile.Parts = ExifParts.ExifTags; image = WriteAndRead(image, imageFormat); - Assert.NotNull(image.MetaData.ExifProfile); - Assert.Equal(8, image.MetaData.ExifProfile.Values.Count()); + Assert.NotNull(image.Metadata.ExifProfile); + Assert.Equal(8, image.Metadata.ExifProfile.Values.Count()); - Assert.NotNull(image.MetaData.ExifProfile.GetValue(ExifTag.ColorSpace)); - Assert.True(image.MetaData.ExifProfile.RemoveValue(ExifTag.ColorSpace)); - Assert.False(image.MetaData.ExifProfile.RemoveValue(ExifTag.ColorSpace)); - Assert.Null(image.MetaData.ExifProfile.GetValue(ExifTag.ColorSpace)); + Assert.NotNull(image.Metadata.ExifProfile.GetValue(ExifTag.ColorSpace)); + Assert.True(image.Metadata.ExifProfile.RemoveValue(ExifTag.ColorSpace)); + Assert.False(image.Metadata.ExifProfile.RemoveValue(ExifTag.ColorSpace)); + Assert.Null(image.Metadata.ExifProfile.GetValue(ExifTag.ColorSpace)); - Assert.Equal(7, image.MetaData.ExifProfile.Values.Count()); + Assert.Equal(7, image.Metadata.ExifProfile.Values.Count()); } [Fact] @@ -234,7 +234,7 @@ namespace SixLabors.ImageSharp.Tests exifProfile.SetValue(ExifTag.XResolution, new Rational(200)); exifProfile.SetValue(ExifTag.YResolution, new Rational(300)); - var metaData = new ImageMetaData + var metaData = new ImageMetadata { ExifProfile = exifProfile, HorizontalResolution = 200, @@ -292,13 +292,13 @@ namespace SixLabors.ImageSharp.Tests ExifProfile expectedProfile = CreateExifProfile(); var expectedProfileTags = expectedProfile.Values.Select(x => x.Tag).ToList(); expectedProfile.SetValue(exifValueToChange, junk.ToString()); - image.MetaData.ExifProfile = expectedProfile; + image.Metadata.ExifProfile = expectedProfile; // act Image reloadedImage = WriteAndRead(image, TestImageWriteFormat.Jpeg); // assert - ExifProfile actualProfile = reloadedImage.MetaData.ExifProfile; + ExifProfile actualProfile = reloadedImage.Metadata.ExifProfile; Assert.NotNull(actualProfile); foreach (ExifTag expectedProfileTag in expectedProfileTags) { @@ -317,7 +317,7 @@ namespace SixLabors.ImageSharp.Tests Image image = TestFile.Create(TestImages.Jpeg.Progressive.Bad.ExifUndefType).CreateImage(); Assert.NotNull(image); - ExifProfile profile = image.MetaData.ExifProfile; + ExifProfile profile = image.Metadata.ExifProfile; Assert.NotNull(profile); foreach (ExifValue value in profile.Values) @@ -335,7 +335,7 @@ namespace SixLabors.ImageSharp.Tests // This images contains array in the exif profile that has zero components. Image image = TestFile.Create(TestImages.Jpeg.Issues.InvalidCast520).CreateImage(); - ExifProfile profile = image.MetaData.ExifProfile; + ExifProfile profile = image.Metadata.ExifProfile; Assert.NotNull(profile); // Force parsing of the profile. @@ -353,13 +353,13 @@ namespace SixLabors.ImageSharp.Tests // arrange var image = new Image(1, 1); ExifProfile expected = CreateExifProfile(); - image.MetaData.ExifProfile = expected; + image.Metadata.ExifProfile = expected; // act Image reloadedImage = WriteAndRead(image, imageFormat); // assert - ExifProfile actual = reloadedImage.MetaData.ExifProfile; + ExifProfile actual = reloadedImage.Metadata.ExifProfile; Assert.NotNull(actual); foreach (KeyValuePair expectedProfileValue in TestProfileValues) { @@ -410,7 +410,7 @@ namespace SixLabors.ImageSharp.Tests { Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateImage(); - ExifProfile profile = image.MetaData.ExifProfile; + ExifProfile profile = image.Metadata.ExifProfile; Assert.NotNull(profile); return profile; diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifReaderTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifReaderTests.cs index c9542a98a9..19ff7d269a 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifReaderTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifReaderTests.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System.Collections.Generic; -using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using SixLabors.ImageSharp.Metadata.Profiles.Exif; using Xunit; namespace SixLabors.ImageSharp.Tests diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifTagDescriptionAttributeTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifTagDescriptionAttributeTests.cs index 2b8d4d7160..144a6e4a33 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifTagDescriptionAttributeTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifTagDescriptionAttributeTests.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using SixLabors.ImageSharp.Metadata.Profiles.Exif; using Xunit; namespace SixLabors.ImageSharp.Tests diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifValueTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifValueTests.cs index 8bc192af5c..4327cae335 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifValueTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifValueTests.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System.Linq; -using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; using Xunit; @@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Tests ExifProfile profile; using (Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateImage()) { - profile = image.MetaData.ExifProfile; + profile = image.Metadata.ExifProfile; } Assert.NotNull(profile); diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.CurvesTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.CurvesTests.cs index beca4db49f..b6fa98b61e 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.CurvesTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.CurvesTests.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Xunit; namespace SixLabors.ImageSharp.Tests.Icc diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.LutTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.LutTests.cs index 43a1ed7f47..04284cb496 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.LutTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.LutTests.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Xunit; namespace SixLabors.ImageSharp.Tests.Icc diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.MatrixTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.MatrixTests.cs index 328cc3fa6a..7987e94102 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.MatrixTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.MatrixTests.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Xunit; namespace SixLabors.ImageSharp.Tests.Icc diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.MultiProcessElementTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.MultiProcessElementTests.cs index 5599e80d19..f9e5428cd4 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.MultiProcessElementTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.MultiProcessElementTests.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Xunit; namespace SixLabors.ImageSharp.Tests.Icc diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.NonPrimitivesTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.NonPrimitivesTests.cs index 86f308ea19..6296390a0a 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.NonPrimitivesTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.NonPrimitivesTests.cs @@ -3,7 +3,7 @@ using System; using System.Numerics; -using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Xunit; namespace SixLabors.ImageSharp.Tests.Icc diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.PrimitivesTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.PrimitivesTests.cs index f8924c43cc..e73ee7c9e9 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.PrimitivesTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.PrimitivesTests.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; -using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Xunit; namespace SixLabors.ImageSharp.Tests.Icc diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntryTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntryTests.cs index aba587846a..dc2c5b6acc 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntryTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntryTests.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Xunit; namespace SixLabors.ImageSharp.Tests.Icc diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReaderTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReaderTests.cs index a3e5a20f68..fc3502e3e3 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReaderTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReaderTests.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; -using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Xunit; namespace SixLabors.ImageSharp.Tests.Icc diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.CurvesTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.CurvesTests.cs index 6a47c988cd..585bda648b 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.CurvesTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.CurvesTests.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Xunit; namespace SixLabors.ImageSharp.Tests.Icc diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.LutTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.LutTests.cs index 9286ac815e..621673ce42 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.LutTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.LutTests.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Xunit; namespace SixLabors.ImageSharp.Tests.Icc diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MatrixTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MatrixTests.cs index 71dd18621c..b43c17f791 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MatrixTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MatrixTests.cs @@ -3,7 +3,7 @@ using System.Numerics; using SixLabors.Memory; -using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Xunit; namespace SixLabors.ImageSharp.Tests.Icc diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MultiProcessElementTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MultiProcessElementTests.cs index 43165c617d..829b556afb 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MultiProcessElementTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MultiProcessElementTests.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Xunit; namespace SixLabors.ImageSharp.Tests.Icc diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.NonPrimitivesTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.NonPrimitivesTests.cs index 1d482e2c1c..ed8a10551e 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.NonPrimitivesTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.NonPrimitivesTests.cs @@ -3,7 +3,7 @@ using System; using System.Numerics; -using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Xunit; namespace SixLabors.ImageSharp.Tests.Icc diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.PrimitivesTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.PrimitivesTests.cs index 845a149b5d..098950be85 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.PrimitivesTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.PrimitivesTests.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; -using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Xunit; namespace SixLabors.ImageSharp.Tests.Icc diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntryTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntryTests.cs index f3ef5effbe..269a8d561b 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntryTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntryTests.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Xunit; namespace SixLabors.ImageSharp.Tests.Icc diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriterTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriterTests.cs index a3f796275a..b7b446699c 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriterTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriterTests.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Xunit; namespace SixLabors.ImageSharp.Tests.Icc diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccProfileTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccProfileTests.cs index 17b5dacc4c..a1944669d0 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccProfileTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccProfileTests.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; -using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Xunit; namespace SixLabors.ImageSharp.Tests.Icc diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccReaderTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccReaderTests.cs index c91076afc9..b4ed52a3d9 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccReaderTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccReaderTests.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Xunit; namespace SixLabors.ImageSharp.Tests.Icc diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccWriterTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccWriterTests.cs index 99ae73f49a..43c27335a9 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccWriterTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccWriterTests.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Xunit; namespace SixLabors.ImageSharp.Tests.Icc diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/Various/IccProfileIdTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/Various/IccProfileIdTests.cs index 46b8b31b40..5c80ac36b6 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/Various/IccProfileIdTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/Various/IccProfileIdTests.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Xunit; namespace SixLabors.ImageSharp.Tests.Icc diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/AutoOrientTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/AutoOrientTests.cs index 9b37fb266a..28b01061ea 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/AutoOrientTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/AutoOrientTests.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; -using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; @@ -45,8 +45,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms { using (Image image = provider.GetImage()) { - image.MetaData.ExifProfile = new ExifProfile(); - image.MetaData.ExifProfile.SetValue(ExifTag.Orientation, orientation); + image.Metadata.ExifProfile = new ExifProfile(); + image.Metadata.ExifProfile.SetValue(ExifTag.Orientation, orientation); image.Mutate(x => x.RotateFlip(rotateType, flipType)); image.DebugSave(provider, string.Join("_", rotateType, flipType, orientation, "1_before")); @@ -75,7 +75,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms using (Image image = provider.GetImage()) { - image.MetaData.ExifProfile = new ExifProfile(bytes); + image.Metadata.ExifProfile = new ExifProfile(bytes); image.Mutate(x => x.AutoOrient()); } } diff --git a/tests/ImageSharp.Tests/Processing/Transforms/TransformsHelpersTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/TransformsHelpersTest.cs index 909e505357..a62f4fc7c6 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/TransformsHelpersTest.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/TransformsHelpersTest.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Transforms; using Xunit; @@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms using (var img = new Image(xy, xy)) { var profile = new ExifProfile(); - img.MetaData.ExifProfile = profile; + img.Metadata.ExifProfile = profile; profile.SetValue(ExifTag.PixelXDimension, (uint)xy); profile.SetValue(ExifTag.PixelYDimension, (uint)xy); diff --git a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataCurves.cs b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataCurves.cs index b39892a81d..334ee026db 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataCurves.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataCurves.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System.Numerics; -using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; namespace SixLabors.ImageSharp.Tests { diff --git a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataLut.cs b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataLut.cs index 4ab0b0f6b0..5ef2156c71 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataLut.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataLut.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; namespace SixLabors.ImageSharp.Tests { diff --git a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataMultiProcessElements.cs b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataMultiProcessElements.cs index fb648198da..586e846801 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataMultiProcessElements.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataMultiProcessElements.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; namespace SixLabors.ImageSharp.Tests { diff --git a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataNonPrimitives.cs b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataNonPrimitives.cs index f16da90c65..f19029f8cc 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataNonPrimitives.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataNonPrimitives.cs @@ -4,7 +4,7 @@ using System; using System.Globalization; using System.Numerics; -using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; namespace SixLabors.ImageSharp.Tests { diff --git a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataProfiles.cs b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataProfiles.cs index b037a7a9af..49ff190467 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataProfiles.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataProfiles.cs @@ -3,7 +3,7 @@ using System; using System.Numerics; -using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; namespace SixLabors.ImageSharp.Tests { diff --git a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataTagDataEntry.cs b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataTagDataEntry.cs index f7c4efa928..b5da224435 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataTagDataEntry.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataTagDataEntry.cs @@ -3,7 +3,7 @@ using System.Globalization; using System.Numerics; -using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; namespace SixLabors.ImageSharp.Tests { diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs index 427a565424..7a775c0817 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs @@ -4,7 +4,7 @@ using System.IO; using SixLabors.ImageSharp.Formats; -using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs @@ -48,7 +48,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs using (var sourceBitmap = new System.Drawing.Bitmap(stream)) { var pixelType = new PixelTypeInfo(System.Drawing.Image.GetPixelFormatSize(sourceBitmap.PixelFormat)); - return new ImageInfo(pixelType, sourceBitmap.Width, sourceBitmap.Height, new ImageMetaData()); + return new ImageInfo(pixelType, sourceBitmap.Width, sourceBitmap.Height, new ImageMetadata()); } } } From c16e443f04dcd7c27da8949d430e98b7cea48ac7 Mon Sep 17 00:00:00 2001 From: Jason Nelson Date: Sat, 13 Apr 2019 07:40:48 -0500 Subject: [PATCH 054/223] Nits - Colorspaces (#880) * Add missing periods * Only perform matrix inversion once in CieXyzToLinearRgbConverter * Eliminate unnessary ToVector3 call * Seal GammaWorkingSpace * Address stylecop violation * Add periods * Fix colorspace companding --- .../ColorSpaces/CieXyChromaticityCoordinates.cs | 2 +- .../ColorSpaces/Companding/GammaCompanding.cs | 2 +- .../ColorSpaces/Companding/LCompanding.cs | 6 +++--- .../ColorSpaces/Companding/Rec2020Companding.cs | 13 +++++++++---- .../ColorSpaces/Companding/Rec709Companding.cs | 6 ++++-- .../Conversion/ColorSpaceConverter.CieLch.cs | 2 -- .../Converters/CieXyzAndCieXyyConverter.cs | 2 +- .../Converters/CieXyzToLinearRgbConverter.cs | 14 +++++++++----- .../Converters/CmykAndRgbConverter.cs | 4 ++-- .../Converters/LinearRgbToRgbConverter.cs | 15 +++++++-------- .../Converters/RgbToLinearRgbConverter.cs | 15 +++++++-------- .../Converters/YCbCrAndRgbConverter.cs | 4 ++-- .../WorkingSpaces/GammaWorkingSpace.cs | 2 +- .../Colorspaces/Companding/CompandingTests.cs | 15 ++++++--------- 14 files changed, 53 insertions(+), 49 deletions(-) diff --git a/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs b/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs index 49c1da9f10..8e14274f66 100644 --- a/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs +++ b/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs @@ -8,7 +8,7 @@ using System.Runtime.CompilerServices; namespace SixLabors.ImageSharp.ColorSpaces { /// - /// Represents the coordinates of CIEXY chromaticity space + /// Represents the coordinates of CIEXY chromaticity space. /// public readonly struct CieXyChromaticityCoordinates : IEquatable { diff --git a/src/ImageSharp/ColorSpaces/Companding/GammaCompanding.cs b/src/ImageSharp/ColorSpaces/Companding/GammaCompanding.cs index 13cca1582d..09b324b00f 100644 --- a/src/ImageSharp/ColorSpaces/Companding/GammaCompanding.cs +++ b/src/ImageSharp/ColorSpaces/Companding/GammaCompanding.cs @@ -7,7 +7,7 @@ using System.Runtime.CompilerServices; namespace SixLabors.ImageSharp.ColorSpaces.Companding { /// - /// Implements gamma companding + /// Implements gamma companding. /// /// /// diff --git a/src/ImageSharp/ColorSpaces/Companding/LCompanding.cs b/src/ImageSharp/ColorSpaces/Companding/LCompanding.cs index 9e2cf8ad86..80b8aee7e7 100644 --- a/src/ImageSharp/ColorSpaces/Companding/LCompanding.cs +++ b/src/ImageSharp/ColorSpaces/Companding/LCompanding.cs @@ -8,7 +8,7 @@ using SixLabors.ImageSharp.ColorSpaces.Conversion; namespace SixLabors.ImageSharp.ColorSpaces.Companding { /// - /// Implements L* companding + /// Implements L* companding. /// /// /// For more info see: @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding /// The representing the linear channel value. [MethodImpl(InliningOptions.ShortMethod)] public static float Expand(float channel) - => channel <= 0.08 ? 100 * channel / CieConstants.Kappa : ImageMaths.Pow3((channel + 0.16F) / 1.16F); + => channel <= 0.08F ? (100F * channel) / CieConstants.Kappa : ImageMaths.Pow3((channel + 0.16F) / 1.16F); /// /// Compresses an uncompanded channel (linear) to its nonlinear equivalent. @@ -33,6 +33,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding /// The representing the nonlinear channel value. [MethodImpl(InliningOptions.ShortMethod)] public static float Compress(float channel) - => channel <= CieConstants.Epsilon ? channel * CieConstants.Kappa / 100F : MathF.Pow(1.16F * channel, 0.3333333F) - 0.16F; + => channel <= CieConstants.Epsilon ? (channel * CieConstants.Kappa) / 100F : (1.16F * MathF.Pow(channel, 0.3333333F)) - 0.16F; } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Companding/Rec2020Companding.cs b/src/ImageSharp/ColorSpaces/Companding/Rec2020Companding.cs index a3a9121727..773fe81647 100644 --- a/src/ImageSharp/ColorSpaces/Companding/Rec2020Companding.cs +++ b/src/ImageSharp/ColorSpaces/Companding/Rec2020Companding.cs @@ -7,14 +7,19 @@ using System.Runtime.CompilerServices; namespace SixLabors.ImageSharp.ColorSpaces.Companding { /// - /// Implements Rec. 2020 companding function (for 12-bits). + /// Implements Rec. 2020 companding function. /// /// /// - /// For 10-bits, companding is identical to /// public static class Rec2020Companding { + private const float Alpha = 1.09929682680944F; + private const float AlphaMinusOne = Alpha - 1F; + private const float Beta = 0.018053968510807F; + private const float InverseBeta = Beta * 4.5F; + private const float Epsilon = 1 / 0.45F; + /// /// Expands a companded channel to its linear equivalent with respect to the energy. /// @@ -22,7 +27,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding /// The representing the linear channel value. [MethodImpl(InliningOptions.ShortMethod)] public static float Expand(float channel) - => channel < 0.08145F ? channel / 4.5F : MathF.Pow((channel + 0.0993F) / 1.0993F, 2.222222F); + => channel < InverseBeta ? channel / 4.5F : MathF.Pow((channel + AlphaMinusOne) / Alpha, Epsilon); /// /// Compresses an uncompanded channel (linear) to its nonlinear equivalent. @@ -31,6 +36,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding /// The representing the nonlinear channel value. [MethodImpl(InliningOptions.ShortMethod)] public static float Compress(float channel) - => channel < 0.0181F ? 4500F * channel : (1.0993F * channel) - 0.0993F; + => channel < Beta ? 4.5F * channel : (Alpha * MathF.Pow(channel, 0.45F)) - AlphaMinusOne; } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Companding/Rec709Companding.cs b/src/ImageSharp/ColorSpaces/Companding/Rec709Companding.cs index e2e802d08a..edc0b9763f 100644 --- a/src/ImageSharp/ColorSpaces/Companding/Rec709Companding.cs +++ b/src/ImageSharp/ColorSpaces/Companding/Rec709Companding.cs @@ -14,6 +14,8 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding /// public static class Rec709Companding { + private const float Epsilon = 1 / 0.45F; + /// /// Expands a companded channel to its linear equivalent with respect to the energy. /// @@ -21,7 +23,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding /// The representing the linear channel value. [MethodImpl(InliningOptions.ShortMethod)] public static float Expand(float channel) - => channel < 0.081F ? channel / 4.5F : MathF.Pow((channel + 0.099F) / 1.099F, 2.222222F); + => channel < 0.081F ? channel / 4.5F : MathF.Pow((channel + 0.099F) / 1.099F, Epsilon); /// /// Compresses an uncompanded channel (linear) to its nonlinear equivalent. @@ -30,6 +32,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding /// The representing the nonlinear channel value. [MethodImpl(InliningOptions.ShortMethod)] public static float Compress(float channel) - => channel < 0.018F ? 4500F * channel : (1.099F * channel) - 0.099F; + => channel < 0.018F ? 4.5F * channel : (1.099F * MathF.Pow(channel, 0.45F)) - 0.099F; } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs index 3810736197..7f4abfa7bb 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs @@ -26,10 +26,8 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// The public CieLch ToCieLch(in CieLab color) { - // Adaptation CieLab adapted = this.Adapt(color); - // Conversion return CieLabToCieLchConverter.Convert(adapted); } diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndCieXyyConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndCieXyyConverter.cs index f33d1ddcc9..7767b7b448 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndCieXyyConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndCieXyyConverter.cs @@ -7,7 +7,7 @@ using System.Runtime.CompilerServices; namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation { /// - /// Color converter between CIE XYZ and CIE xyY + /// Color converter between CIE XYZ and CIE xyY. /// for formulas. /// internal sealed class CieXyzAndCieXyyConverter diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToLinearRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToLinearRgbConverter.cs index 3812cdbdd8..f166e4c14a 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToLinearRgbConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToLinearRgbConverter.cs @@ -28,11 +28,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation public CieXyzToLinearRgbConverter(RgbWorkingSpaceBase workingSpace) { this.TargetWorkingSpace = workingSpace; - this.conversionMatrix = GetRgbToCieXyzMatrix(workingSpace); + + // Gets the inverted Rgb -> Xyz matrix + Matrix4x4.Invert(GetRgbToCieXyzMatrix(workingSpace), out Matrix4x4 inverted); + + this.conversionMatrix = inverted; } /// - /// Gets the target working space + /// Gets the target working space. /// public RgbWorkingSpaceBase TargetWorkingSpace { get; } @@ -40,12 +44,12 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation /// Performs the conversion from the input to an instance of type. /// /// The input color instance. - /// The converted result + /// The converted result. [MethodImpl(InliningOptions.ShortMethod)] public LinearRgb Convert(in CieXyz input) { - Matrix4x4.Invert(this.conversionMatrix, out Matrix4x4 inverted); - var vector = Vector3.Transform(input.ToVector3(), inverted); + var vector = Vector3.Transform(input.ToVector3(), this.conversionMatrix); + return new LinearRgb(vector, this.TargetWorkingSpace); } } diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CmykAndRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CmykAndRgbConverter.cs index 29fd32905b..0700dab43a 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CmykAndRgbConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CmykAndRgbConverter.cs @@ -8,7 +8,7 @@ using System.Runtime.CompilerServices; namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation { /// - /// Color converter between and + /// Color converter between and . /// internal sealed class CmykAndRgbConverter { @@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation /// Performs the conversion from the input to an instance of type. /// /// The input color instance. - /// The converted result + /// The converted result. [MethodImpl(InliningOptions.ShortMethod)] public Cmyk Convert(in Rgb input) { diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToRgbConverter.cs index 1cc055bee2..8454430935 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToRgbConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToRgbConverter.cs @@ -6,7 +6,7 @@ using System.Runtime.CompilerServices; namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation { /// - /// Color converter between and + /// Color converter between and . /// internal sealed class LinearRgbToRgbConverter { @@ -14,16 +14,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation /// Performs the conversion from the input to an instance of type. /// /// The input color instance. - /// The converted result + /// The converted result. [MethodImpl(InliningOptions.ShortMethod)] public Rgb Convert(in LinearRgb input) { - var vector = input.ToVector3(); - vector.X = input.WorkingSpace.Compress(vector.X); - vector.Y = input.WorkingSpace.Compress(vector.Y); - vector.Z = input.WorkingSpace.Compress(vector.Z); - - return new Rgb(vector, input.WorkingSpace); + return new Rgb( + r: input.WorkingSpace.Compress(input.R), + g: input.WorkingSpace.Compress(input.G), + b: input.WorkingSpace.Compress(input.B), + workingSpace: input.WorkingSpace); } } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/RgbToLinearRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/RgbToLinearRgbConverter.cs index 03912a421e..4ddbe42e54 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/RgbToLinearRgbConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/RgbToLinearRgbConverter.cs @@ -6,7 +6,7 @@ using System.Runtime.CompilerServices; namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation { /// - /// Color converter between Rgb and LinearRgb + /// Color converter between Rgb and LinearRgb. /// internal class RgbToLinearRgbConverter { @@ -14,16 +14,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation /// Performs the conversion from the input to an instance of type. /// /// The input color instance. - /// The converted result + /// The converted result. [MethodImpl(InliningOptions.ShortMethod)] public LinearRgb Convert(in Rgb input) { - var vector = input.ToVector3(); - vector.X = input.WorkingSpace.Expand(vector.X); - vector.Y = input.WorkingSpace.Expand(vector.Y); - vector.Z = input.WorkingSpace.Expand(vector.Z); - - return new LinearRgb(vector, input.WorkingSpace); + return new LinearRgb( + r: input.WorkingSpace.Expand(input.R), + g: input.WorkingSpace.Expand(input.G), + b: input.WorkingSpace.Expand(input.B), + workingSpace: input.WorkingSpace); } } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/YCbCrAndRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/YCbCrAndRgbConverter.cs index 4ac3ad3cf9..ee15ffa508 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/YCbCrAndRgbConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/YCbCrAndRgbConverter.cs @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation /// Performs the conversion from the input to an instance of type. /// /// The input color instance. - /// The converted result + /// The converted result. [MethodImpl(InliningOptions.ShortMethod)] public Rgb Convert(in YCbCr input) { @@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation /// Performs the conversion from the input to an instance of type. /// /// The input color instance. - /// The converted result + /// The converted result. [MethodImpl(InliningOptions.ShortMethod)] public YCbCr Convert(in Rgb input) { diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs index 639d0b2933..c45dea3179 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs @@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation /// /// The gamma working space. /// - public class GammaWorkingSpace : RgbWorkingSpaceBase + public sealed class GammaWorkingSpace : RgbWorkingSpaceBase { /// /// Initializes a new instance of the class. diff --git a/tests/ImageSharp.Tests/Colorspaces/Companding/CompandingTests.cs b/tests/ImageSharp.Tests/Colorspaces/Companding/CompandingTests.cs index b2ee030483..e1386e1a09 100644 --- a/tests/ImageSharp.Tests/Colorspaces/Companding/CompandingTests.cs +++ b/tests/ImageSharp.Tests/Colorspaces/Companding/CompandingTests.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Linq; using System.Numerics; using SixLabors.ImageSharp.ColorSpaces.Companding; using Xunit; @@ -10,9 +9,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Colorspaces.Companding { /// - /// Tests various companding algorithms. Numbers are hand calculated from formulas online. - /// TODO: Oddly the formula for converting to/from Rec 2020 and 709 from Wikipedia seems to cause the value to - /// fail a round trip. They're large spaces so this is a surprise. More reading required!! + /// Tests various companding algorithms. Expanded numbers are hand calculated from formulas online. /// public class CompandingTests { @@ -24,7 +21,7 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces.Companding const float input = .667F; float e = Rec2020Companding.Expand(input); float c = Rec2020Companding.Compress(e); - CompandingIsCorrectImpl(e, c, .4484759F, .3937096F); + CompandingIsCorrectImpl(e, c, .4484759F, input); } [Fact] @@ -33,7 +30,7 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces.Companding const float input = .667F; float e = Rec709Companding.Expand(input); float c = Rec709Companding.Compress(e); - CompandingIsCorrectImpl(e, c, .4483577F, .3937451F); + CompandingIsCorrectImpl(e, c, .4483577F, input); } [Fact] @@ -42,7 +39,7 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces.Companding const float input = .667F; float e = SRgbCompanding.Expand(input); float c = SRgbCompanding.Compress(e); - CompandingIsCorrectImpl(e, c, .40242353F, .667F); + CompandingIsCorrectImpl(e, c, .40242353F, input); } [Theory] @@ -98,7 +95,7 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces.Companding const float input = .667F; float e = GammaCompanding.Expand(input, gamma); float c = GammaCompanding.Compress(e, gamma); - CompandingIsCorrectImpl(e, c, .41027668F, .667F); + CompandingIsCorrectImpl(e, c, .41027668F, input); } [Fact] @@ -107,7 +104,7 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces.Companding const float input = .667F; float e = LCompanding.Expand(input); float c = LCompanding.Compress(e); - CompandingIsCorrectImpl(e, c, .36236193F, .58908917F); + CompandingIsCorrectImpl(e, c, .36236193F, input); } private static void CompandingIsCorrectImpl(float e, float c, float expanded, float compressed) From 48a1a1f107c4975439f63852bfc90aa9a5e1dece Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 14 Apr 2019 08:01:38 +1000 Subject: [PATCH 055/223] Use more accuracy when calculating variance. Fix #866 (#874) * Use more accuracy when calculating variance. Fix #866 * Add unit tests * Add test that fails with old image. * Make IFrameQuantizer IDisposable * Update GifEncoderCore.cs --- src/ImageSharp/Formats/Gif/GifEncoderCore.cs | 24 +- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 7 +- .../FrameQuantizerBase{TPixel}.cs | 5 + .../Quantization/IFrameQuantizer{TPixel}.cs | 3 +- .../Quantization/QuantizeProcessor.cs | 2 +- .../Quantization/WuFrameQuantizer{TPixel}.cs | 416 ++++++++++-------- .../Quantization/WuQuantizerTests.cs | 168 +++++++ tests/ImageSharp.Tests/TestImages.cs | 1 + tests/Images/Input/Png/low-variance.png | 3 + 9 files changed, 425 insertions(+), 204 deletions(-) create mode 100644 tests/ImageSharp.Tests/Quantization/WuQuantizerTests.cs create mode 100644 tests/Images/Input/Png/low-variance.png diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs index e5a5463ebb..12a515cca7 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs @@ -95,8 +95,11 @@ namespace SixLabors.ImageSharp.Formats.Gif bool useGlobalTable = this.colorTableMode == GifColorTableMode.Global; // Quantize the image returning a palette. - QuantizedFrame quantized = - this.quantizer.CreateFrameQuantizer(image.GetConfiguration()).QuantizeFrame(image.Frames.RootFrame); + QuantizedFrame quantized = null; + using (IFrameQuantizer frameQuantizer = this.quantizer.CreateFrameQuantizer(image.GetConfiguration())) + { + quantized = frameQuantizer.QuantizeFrame(image.Frames.RootFrame); + } // Get the number of bits. this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(quantized.Palette.Length).Clamp(1, 8); @@ -133,7 +136,6 @@ namespace SixLabors.ImageSharp.Formats.Gif // Clean up. quantized?.Dispose(); - quantized = null; // TODO: Write extension etc stream.WriteByte(GifConstants.EndIntroducer); @@ -158,7 +160,8 @@ namespace SixLabors.ImageSharp.Formats.Gif } else { - using (QuantizedFrame paletteQuantized = palleteQuantizer.CreateFrameQuantizer(image.GetConfiguration()).QuantizeFrame(frame)) + using (IFrameQuantizer palleteFrameQuantizer = palleteQuantizer.CreateFrameQuantizer(image.GetConfiguration())) + using (QuantizedFrame paletteQuantized = palleteFrameQuantizer.QuantizeFrame(frame)) { this.WriteImageData(paletteQuantized, stream); } @@ -181,14 +184,17 @@ namespace SixLabors.ImageSharp.Formats.Gif if (previousFrame != null && previousMeta.ColorTableLength != frameMetadata.ColorTableLength && frameMetadata.ColorTableLength > 0) { - quantized = this.quantizer.CreateFrameQuantizer( - image.GetConfiguration(), - frameMetadata.ColorTableLength).QuantizeFrame(frame); + using (IFrameQuantizer frameQuantizer = this.quantizer.CreateFrameQuantizer(image.GetConfiguration(), frameMetadata.ColorTableLength)) + { + quantized = frameQuantizer.QuantizeFrame(frame); + } } else { - quantized = this.quantizer.CreateFrameQuantizer(image.GetConfiguration()) - .QuantizeFrame(frame); + using (IFrameQuantizer frameQuantizer = this.quantizer.CreateFrameQuantizer(image.GetConfiguration())) + { + quantized = frameQuantizer.QuantizeFrame(frame); + } } } diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 9818e6cf17..7415b07532 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -243,8 +243,11 @@ namespace SixLabors.ImageSharp.Formats.Png } // Create quantized frame returning the palette and set the bit depth. - quantized = this.quantizer.CreateFrameQuantizer(image.GetConfiguration()) - .QuantizeFrame(image.Frames.RootFrame); + using (IFrameQuantizer frameQuantizer = this.quantizer.CreateFrameQuantizer(image.GetConfiguration())) + { + quantized = frameQuantizer.QuantizeFrame(image.Frames.RootFrame); + } + byte quantizedBits = (byte)ImageMaths.GetBitsNeededForColorDepth(quantized.Palette.Length).Clamp(1, 8); bits = Math.Max(bits, quantizedBits); diff --git a/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizerBase{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizerBase{TPixel}.cs index a70cfb6601..f23343f6d7 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizerBase{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizerBase{TPixel}.cs @@ -98,6 +98,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization return quantizedFrame; } + /// + public virtual void Dispose() + { + } + /// /// Execute the first pass through the pixels in the image to create the palette. /// diff --git a/src/ImageSharp/Processing/Processors/Quantization/IFrameQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/IFrameQuantizer{TPixel}.cs index 50fdb5b587..f0b68b3a08 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/IFrameQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/IFrameQuantizer{TPixel}.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Dithering; @@ -10,7 +11,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// Provides methods to allow the execution of the quantization process on an image frame. /// /// The pixel format. - public interface IFrameQuantizer + public interface IFrameQuantizer : IDisposable where TPixel : struct, IPixel { /// diff --git a/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor.cs b/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor.cs index 8da89bf94a..e99f504b42 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor.cs @@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - IFrameQuantizer executor = this.Quantizer.CreateFrameQuantizer(configuration); + using (IFrameQuantizer executor = this.Quantizer.CreateFrameQuantizer(configuration)) using (QuantizedFrame quantized = executor.QuantizeFrame(source)) { int paletteCount = quantized.Palette.Length - 1; diff --git a/src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs index 44df226cfd..1f1513adf1 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs @@ -11,6 +11,8 @@ using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Memory; +// TODO: Isn't an AOS ("array of structures") layout more efficient & more readable than SOA ("structure of arrays") for this particular use case? +// (T, R, G, B, A, M2) could be grouped together! Investigate a ColorMoment struct. namespace SixLabors.ImageSharp.Processing.Processors.Quantization { /// @@ -36,20 +38,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization internal sealed class WuFrameQuantizer : FrameQuantizerBase where TPixel : struct, IPixel { - // TODO: The WuFrameQuantizer code is rising several questions: - // - Do we really need to ALWAYS allocate the whole table of size TableLength? (~ 2471625 * sizeof(long) * 5 bytes ) JS. I'm afraid so. - // - Isn't an AOS ("array of structures") layout more efficient & more readable than SOA ("structure of arrays") for this particular use case? - // (T, R, G, B, A, M2) could be grouped together! - // - It's a frequently used class, we need tests! (So we can optimize safely.) There are tests in the original!!! We should just adopt them! - // https://github.com/JeremyAnsel/JeremyAnsel.ColorQuant/blob/master/JeremyAnsel.ColorQuant/JeremyAnsel.ColorQuant.Tests/WuColorQuantizerTests.cs + // The following two variables determine the amount of bits to preserve when calculating the histogram. + // Reducing the value of these numbers the granularity of the color maps produced, making it much faster + // and using much less memory but potentially less accurate. Current results are very good though! /// - /// The index bits. + /// The index bits. 6 in original code. /// private const int IndexBits = 5; /// - /// The index alpha bits. Keep separate for now to allow easy adjustment. + /// The index alpha bits. 3 in original code. /// private const int IndexAlphaBits = 5; @@ -64,7 +63,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization private const int IndexAlphaCount = (1 << IndexAlphaBits) + 1; /// - /// The table length. Now 1185921. + /// The table length. Now 1185921. originally 2471625. /// private const int TableLength = IndexCount * IndexCount * IndexCount * IndexAlphaCount; @@ -96,7 +95,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// /// Moment of c^2*P(c). /// - private IMemoryOwner m2; + private IMemoryOwner m2; /// /// Color space tag. @@ -149,28 +148,27 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization Guard.NotNull(image, nameof(image)); MemoryAllocator memoryAllocator = image.MemoryAllocator; - try - { - this.vwt = memoryAllocator.Allocate(TableLength, AllocationOptions.Clean); - this.vmr = memoryAllocator.Allocate(TableLength, AllocationOptions.Clean); - this.vmg = memoryAllocator.Allocate(TableLength, AllocationOptions.Clean); - this.vmb = memoryAllocator.Allocate(TableLength, AllocationOptions.Clean); - this.vma = memoryAllocator.Allocate(TableLength, AllocationOptions.Clean); - this.m2 = memoryAllocator.Allocate(TableLength, AllocationOptions.Clean); - this.tag = memoryAllocator.Allocate(TableLength, AllocationOptions.Clean); - - return base.QuantizeFrame(image); - } - finally - { - this.vwt?.Dispose(); - this.vmr?.Dispose(); - this.vmg?.Dispose(); - this.vmb?.Dispose(); - this.vma?.Dispose(); - this.m2?.Dispose(); - this.tag?.Dispose(); - } + this.vwt = memoryAllocator.Allocate(TableLength, AllocationOptions.Clean); + this.vmr = memoryAllocator.Allocate(TableLength, AllocationOptions.Clean); + this.vmg = memoryAllocator.Allocate(TableLength, AllocationOptions.Clean); + this.vmb = memoryAllocator.Allocate(TableLength, AllocationOptions.Clean); + this.vma = memoryAllocator.Allocate(TableLength, AllocationOptions.Clean); + this.m2 = memoryAllocator.Allocate(TableLength, AllocationOptions.Clean); + this.tag = memoryAllocator.Allocate(TableLength, AllocationOptions.Clean); + + return base.QuantizeFrame(image); + } + + /// + public override void Dispose() + { + this.vwt?.Dispose(); + this.vmr?.Dispose(); + this.vmg?.Dispose(); + this.vmb?.Dispose(); + this.vma?.Dispose(); + this.m2?.Dispose(); + this.tag?.Dispose(); } internal TPixel[] AotGetPalette() => this.GetPalette(); @@ -275,9 +273,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int GetPaletteIndex(int r, int g, int b, int a) { - return (r << ((IndexBits * 2) + IndexAlphaBits)) + (r << (IndexBits + IndexAlphaBits + 1)) - + (g << (IndexBits + IndexAlphaBits)) + (r << (IndexBits * 2)) + (r << (IndexBits + 1)) - + (g << IndexBits) + ((r + g + b) << IndexAlphaBits) + r + g + b + a; + return (r << ((IndexBits * 2) + IndexAlphaBits)) + + (r << (IndexBits + IndexAlphaBits + 1)) + + (g << (IndexBits + IndexAlphaBits)) + + (r << (IndexBits * 2)) + + (r << (IndexBits + 1)) + + (g << IndexBits) + + ((r + g + b) << IndexAlphaBits) + + r + g + b + a; } /// @@ -288,26 +291,26 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// The result. private static float Volume(ref Box cube, Span moment) { - return moment[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A1)] - - moment[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A0)] - - moment[GetPaletteIndex(cube.R1, cube.G1, cube.B0, cube.A1)] - + moment[GetPaletteIndex(cube.R1, cube.G1, cube.B0, cube.A0)] - - moment[GetPaletteIndex(cube.R1, cube.G0, cube.B1, cube.A1)] - + moment[GetPaletteIndex(cube.R1, cube.G0, cube.B1, cube.A0)] - + moment[GetPaletteIndex(cube.R1, cube.G0, cube.B0, cube.A1)] - - moment[GetPaletteIndex(cube.R1, cube.G0, cube.B0, cube.A0)] - - moment[GetPaletteIndex(cube.R0, cube.G1, cube.B1, cube.A1)] - + moment[GetPaletteIndex(cube.R0, cube.G1, cube.B1, cube.A0)] - + moment[GetPaletteIndex(cube.R0, cube.G1, cube.B0, cube.A1)] - - moment[GetPaletteIndex(cube.R0, cube.G1, cube.B0, cube.A0)] - + moment[GetPaletteIndex(cube.R0, cube.G0, cube.B1, cube.A1)] - - moment[GetPaletteIndex(cube.R0, cube.G0, cube.B1, cube.A0)] - - moment[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A1)] - + moment[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A0)]; + return moment[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMax, cube.AMax)] + - moment[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMax, cube.AMin)] + - moment[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMin, cube.AMax)] + + moment[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMin, cube.AMin)] + - moment[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMax, cube.AMax)] + + moment[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMax, cube.AMin)] + + moment[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMin, cube.AMax)] + - moment[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMin, cube.AMin)] + - moment[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMax, cube.AMax)] + + moment[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMax, cube.AMin)] + + moment[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMin, cube.AMax)] + - moment[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMin, cube.AMin)] + + moment[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMax, cube.AMax)] + - moment[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMax, cube.AMin)] + - moment[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMax)] + + moment[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMin)]; } /// - /// Computes part of Volume(cube, moment) that doesn't depend on r1, g1, or b1 (depending on direction). + /// Computes part of Volume(cube, moment) that doesn't depend on RMax, GMax, BMax, or AMax (depending on direction). /// /// The cube. /// The direction. @@ -319,47 +322,47 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization { // Red case 3: - return -moment[GetPaletteIndex(cube.R0, cube.G1, cube.B1, cube.A1)] - + moment[GetPaletteIndex(cube.R0, cube.G1, cube.B1, cube.A0)] - + moment[GetPaletteIndex(cube.R0, cube.G1, cube.B0, cube.A1)] - - moment[GetPaletteIndex(cube.R0, cube.G1, cube.B0, cube.A0)] - + moment[GetPaletteIndex(cube.R0, cube.G0, cube.B1, cube.A1)] - - moment[GetPaletteIndex(cube.R0, cube.G0, cube.B1, cube.A0)] - - moment[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A1)] - + moment[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A0)]; + return -moment[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMax, cube.AMax)] + + moment[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMax, cube.AMin)] + + moment[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMin, cube.AMax)] + - moment[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMin, cube.AMin)] + + moment[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMax, cube.AMax)] + - moment[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMax, cube.AMin)] + - moment[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMax)] + + moment[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMin)]; // Green case 2: - return -moment[GetPaletteIndex(cube.R1, cube.G0, cube.B1, cube.A1)] - + moment[GetPaletteIndex(cube.R1, cube.G0, cube.B1, cube.A0)] - + moment[GetPaletteIndex(cube.R1, cube.G0, cube.B0, cube.A1)] - - moment[GetPaletteIndex(cube.R1, cube.G0, cube.B0, cube.A0)] - + moment[GetPaletteIndex(cube.R0, cube.G0, cube.B1, cube.A1)] - - moment[GetPaletteIndex(cube.R0, cube.G0, cube.B1, cube.A0)] - - moment[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A1)] - + moment[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A0)]; + return -moment[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMax, cube.AMax)] + + moment[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMax, cube.AMin)] + + moment[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMin, cube.AMax)] + - moment[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMin, cube.AMin)] + + moment[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMax, cube.AMax)] + - moment[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMax, cube.AMin)] + - moment[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMax)] + + moment[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMin)]; // Blue case 1: - return -moment[GetPaletteIndex(cube.R1, cube.G1, cube.B0, cube.A1)] - + moment[GetPaletteIndex(cube.R1, cube.G1, cube.B0, cube.A0)] - + moment[GetPaletteIndex(cube.R1, cube.G0, cube.B0, cube.A1)] - - moment[GetPaletteIndex(cube.R1, cube.G0, cube.B0, cube.A0)] - + moment[GetPaletteIndex(cube.R0, cube.G1, cube.B0, cube.A1)] - - moment[GetPaletteIndex(cube.R0, cube.G1, cube.B0, cube.A0)] - - moment[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A1)] - + moment[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A0)]; + return -moment[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMin, cube.AMax)] + + moment[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMin, cube.AMin)] + + moment[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMin, cube.AMax)] + - moment[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMin, cube.AMin)] + + moment[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMin, cube.AMax)] + - moment[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMin, cube.AMin)] + - moment[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMax)] + + moment[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMin)]; // Alpha case 0: - return -moment[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A0)] - + moment[GetPaletteIndex(cube.R1, cube.G1, cube.B0, cube.A0)] - + moment[GetPaletteIndex(cube.R1, cube.G0, cube.B1, cube.A0)] - - moment[GetPaletteIndex(cube.R1, cube.G0, cube.B0, cube.A0)] - + moment[GetPaletteIndex(cube.R0, cube.G1, cube.B1, cube.A0)] - - moment[GetPaletteIndex(cube.R0, cube.G1, cube.B0, cube.A0)] - - moment[GetPaletteIndex(cube.R0, cube.G0, cube.B1, cube.A0)] - + moment[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A0)]; + return -moment[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMax, cube.AMin)] + + moment[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMin, cube.AMin)] + + moment[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMax, cube.AMin)] + - moment[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMin, cube.AMin)] + + moment[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMax, cube.AMin)] + - moment[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMin, cube.AMin)] + - moment[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMax, cube.AMin)] + + moment[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMin)]; default: throw new ArgumentOutOfRangeException(nameof(direction)); @@ -367,7 +370,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization } /// - /// Computes remainder of Volume(cube, moment), substituting position for r1, g1, or b1 (depending on direction). + /// Computes remainder of Volume(cube, moment), substituting position for RMax, GMax, BMax, or AMax (depending on direction). /// /// The cube. /// The direction. @@ -380,47 +383,47 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization { // Red case 3: - return moment[GetPaletteIndex(position, cube.G1, cube.B1, cube.A1)] - - moment[GetPaletteIndex(position, cube.G1, cube.B1, cube.A0)] - - moment[GetPaletteIndex(position, cube.G1, cube.B0, cube.A1)] - + moment[GetPaletteIndex(position, cube.G1, cube.B0, cube.A0)] - - moment[GetPaletteIndex(position, cube.G0, cube.B1, cube.A1)] - + moment[GetPaletteIndex(position, cube.G0, cube.B1, cube.A0)] - + moment[GetPaletteIndex(position, cube.G0, cube.B0, cube.A1)] - - moment[GetPaletteIndex(position, cube.G0, cube.B0, cube.A0)]; + return moment[GetPaletteIndex(position, cube.GMax, cube.BMax, cube.AMax)] + - moment[GetPaletteIndex(position, cube.GMax, cube.BMax, cube.AMin)] + - moment[GetPaletteIndex(position, cube.GMax, cube.BMin, cube.AMax)] + + moment[GetPaletteIndex(position, cube.GMax, cube.BMin, cube.AMin)] + - moment[GetPaletteIndex(position, cube.GMin, cube.BMax, cube.AMax)] + + moment[GetPaletteIndex(position, cube.GMin, cube.BMax, cube.AMin)] + + moment[GetPaletteIndex(position, cube.GMin, cube.BMin, cube.AMax)] + - moment[GetPaletteIndex(position, cube.GMin, cube.BMin, cube.AMin)]; // Green case 2: - return moment[GetPaletteIndex(cube.R1, position, cube.B1, cube.A1)] - - moment[GetPaletteIndex(cube.R1, position, cube.B1, cube.A0)] - - moment[GetPaletteIndex(cube.R1, position, cube.B0, cube.A1)] - + moment[GetPaletteIndex(cube.R1, position, cube.B0, cube.A0)] - - moment[GetPaletteIndex(cube.R0, position, cube.B1, cube.A1)] - + moment[GetPaletteIndex(cube.R0, position, cube.B1, cube.A0)] - + moment[GetPaletteIndex(cube.R0, position, cube.B0, cube.A1)] - - moment[GetPaletteIndex(cube.R0, position, cube.B0, cube.A0)]; + return moment[GetPaletteIndex(cube.RMax, position, cube.BMax, cube.AMax)] + - moment[GetPaletteIndex(cube.RMax, position, cube.BMax, cube.AMin)] + - moment[GetPaletteIndex(cube.RMax, position, cube.BMin, cube.AMax)] + + moment[GetPaletteIndex(cube.RMax, position, cube.BMin, cube.AMin)] + - moment[GetPaletteIndex(cube.RMin, position, cube.BMax, cube.AMax)] + + moment[GetPaletteIndex(cube.RMin, position, cube.BMax, cube.AMin)] + + moment[GetPaletteIndex(cube.RMin, position, cube.BMin, cube.AMax)] + - moment[GetPaletteIndex(cube.RMin, position, cube.BMin, cube.AMin)]; // Blue case 1: - return moment[GetPaletteIndex(cube.R1, cube.G1, position, cube.A1)] - - moment[GetPaletteIndex(cube.R1, cube.G1, position, cube.A0)] - - moment[GetPaletteIndex(cube.R1, cube.G0, position, cube.A1)] - + moment[GetPaletteIndex(cube.R1, cube.G0, position, cube.A0)] - - moment[GetPaletteIndex(cube.R0, cube.G1, position, cube.A1)] - + moment[GetPaletteIndex(cube.R0, cube.G1, position, cube.A0)] - + moment[GetPaletteIndex(cube.R0, cube.G0, position, cube.A1)] - - moment[GetPaletteIndex(cube.R0, cube.G0, position, cube.A0)]; + return moment[GetPaletteIndex(cube.RMax, cube.GMax, position, cube.AMax)] + - moment[GetPaletteIndex(cube.RMax, cube.GMax, position, cube.AMin)] + - moment[GetPaletteIndex(cube.RMax, cube.GMin, position, cube.AMax)] + + moment[GetPaletteIndex(cube.RMax, cube.GMin, position, cube.AMin)] + - moment[GetPaletteIndex(cube.RMin, cube.GMax, position, cube.AMax)] + + moment[GetPaletteIndex(cube.RMin, cube.GMax, position, cube.AMin)] + + moment[GetPaletteIndex(cube.RMin, cube.GMin, position, cube.AMax)] + - moment[GetPaletteIndex(cube.RMin, cube.GMin, position, cube.AMin)]; // Alpha case 0: - return moment[GetPaletteIndex(cube.R1, cube.G1, cube.B1, position)] - - moment[GetPaletteIndex(cube.R1, cube.G1, cube.B0, position)] - - moment[GetPaletteIndex(cube.R1, cube.G0, cube.B1, position)] - + moment[GetPaletteIndex(cube.R1, cube.G0, cube.B0, position)] - - moment[GetPaletteIndex(cube.R0, cube.G1, cube.B1, position)] - + moment[GetPaletteIndex(cube.R0, cube.G1, cube.B0, position)] - + moment[GetPaletteIndex(cube.R0, cube.G0, cube.B1, position)] - - moment[GetPaletteIndex(cube.R0, cube.G0, cube.B0, position)]; + return moment[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMax, position)] + - moment[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMin, position)] + - moment[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMax, position)] + + moment[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMin, position)] + - moment[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMax, position)] + + moment[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMin, position)] + + moment[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMax, position)] + - moment[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, position)]; default: throw new ArgumentOutOfRangeException(nameof(direction)); @@ -440,7 +443,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization Span vmgSpan = this.vmg.GetSpan(); Span vmbSpan = this.vmb.GetSpan(); Span vmaSpan = this.vma.GetSpan(); - Span m2Span = this.m2.GetSpan(); + Span m2Span = this.m2.GetSpan(); // Build up the 3-D color histogram // Loop through each row @@ -489,34 +492,34 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization Span vmgSpan = this.vmg.GetSpan(); Span vmbSpan = this.vmb.GetSpan(); Span vmaSpan = this.vma.GetSpan(); - Span m2Span = this.m2.GetSpan(); + Span m2Span = this.m2.GetSpan(); using (IMemoryOwner volume = memoryAllocator.Allocate(IndexCount * IndexAlphaCount)) using (IMemoryOwner volumeR = memoryAllocator.Allocate(IndexCount * IndexAlphaCount)) using (IMemoryOwner volumeG = memoryAllocator.Allocate(IndexCount * IndexAlphaCount)) using (IMemoryOwner volumeB = memoryAllocator.Allocate(IndexCount * IndexAlphaCount)) using (IMemoryOwner volumeA = memoryAllocator.Allocate(IndexCount * IndexAlphaCount)) - using (IMemoryOwner volume2 = memoryAllocator.Allocate(IndexCount * IndexAlphaCount)) + using (IMemoryOwner volume2 = memoryAllocator.Allocate(IndexCount * IndexAlphaCount)) using (IMemoryOwner area = memoryAllocator.Allocate(IndexAlphaCount)) using (IMemoryOwner areaR = memoryAllocator.Allocate(IndexAlphaCount)) using (IMemoryOwner areaG = memoryAllocator.Allocate(IndexAlphaCount)) using (IMemoryOwner areaB = memoryAllocator.Allocate(IndexAlphaCount)) using (IMemoryOwner areaA = memoryAllocator.Allocate(IndexAlphaCount)) - using (IMemoryOwner area2 = memoryAllocator.Allocate(IndexAlphaCount)) + using (IMemoryOwner area2 = memoryAllocator.Allocate(IndexAlphaCount)) { Span volumeSpan = volume.GetSpan(); Span volumeRSpan = volumeR.GetSpan(); Span volumeGSpan = volumeG.GetSpan(); Span volumeBSpan = volumeB.GetSpan(); Span volumeASpan = volumeA.GetSpan(); - Span volume2Span = volume2.GetSpan(); + Span volume2Span = volume2.GetSpan(); Span areaSpan = area.GetSpan(); Span areaRSpan = areaR.GetSpan(); Span areaGSpan = areaG.GetSpan(); Span areaBSpan = areaB.GetSpan(); Span areaASpan = areaA.GetSpan(); - Span area2Span = area2.GetSpan(); + Span area2Span = area2.GetSpan(); for (int r = 1; r < IndexCount; r++) { @@ -543,7 +546,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization long lineG = 0; long lineB = 0; long lineA = 0; - float line2 = 0; + double line2 = 0; for (int a = 1; a < IndexAlphaCount; a++) { @@ -592,35 +595,35 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// /// The cube. /// The . - private float Variance(ref Box cube) + private double Variance(ref Box cube) { float dr = Volume(ref cube, this.vmr.GetSpan()); float dg = Volume(ref cube, this.vmg.GetSpan()); float db = Volume(ref cube, this.vmb.GetSpan()); float da = Volume(ref cube, this.vma.GetSpan()); - Span m2Span = this.m2.GetSpan(); - - float xx = - m2Span[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A1)] - - m2Span[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A0)] - - m2Span[GetPaletteIndex(cube.R1, cube.G1, cube.B0, cube.A1)] - + m2Span[GetPaletteIndex(cube.R1, cube.G1, cube.B0, cube.A0)] - - m2Span[GetPaletteIndex(cube.R1, cube.G0, cube.B1, cube.A1)] - + m2Span[GetPaletteIndex(cube.R1, cube.G0, cube.B1, cube.A0)] - + m2Span[GetPaletteIndex(cube.R1, cube.G0, cube.B0, cube.A1)] - - m2Span[GetPaletteIndex(cube.R1, cube.G0, cube.B0, cube.A0)] - - m2Span[GetPaletteIndex(cube.R0, cube.G1, cube.B1, cube.A1)] - + m2Span[GetPaletteIndex(cube.R0, cube.G1, cube.B1, cube.A0)] - + m2Span[GetPaletteIndex(cube.R0, cube.G1, cube.B0, cube.A1)] - - m2Span[GetPaletteIndex(cube.R0, cube.G1, cube.B0, cube.A0)] - + m2Span[GetPaletteIndex(cube.R0, cube.G0, cube.B1, cube.A1)] - - m2Span[GetPaletteIndex(cube.R0, cube.G0, cube.B1, cube.A0)] - - m2Span[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A1)] - + m2Span[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A0)]; + Span m2Span = this.m2.GetSpan(); + + double moment = + m2Span[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMax, cube.AMax)] + - m2Span[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMax, cube.AMin)] + - m2Span[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMin, cube.AMax)] + + m2Span[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMin, cube.AMin)] + - m2Span[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMax, cube.AMax)] + + m2Span[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMax, cube.AMin)] + + m2Span[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMin, cube.AMax)] + - m2Span[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMin, cube.AMin)] + - m2Span[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMax, cube.AMax)] + + m2Span[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMax, cube.AMin)] + + m2Span[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMin, cube.AMax)] + - m2Span[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMin, cube.AMin)] + + m2Span[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMax, cube.AMax)] + - m2Span[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMax, cube.AMin)] + - m2Span[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMax)] + + m2Span[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMin)]; var vector = new Vector4(dr, dg, db, da); - return xx - (Vector4.Dot(vector, vector) / Volume(ref cube, this.vwt.GetSpan())); + return moment - (Vector4.Dot(vector, vector) / Volume(ref cube, this.vwt.GetSpan())); } /// @@ -714,10 +717,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization float wholeA = Volume(ref set1, this.vma.GetSpan()); float wholeW = Volume(ref set1, this.vwt.GetSpan()); - float maxr = this.Maximize(ref set1, 3, set1.R0 + 1, set1.R1, out int cutr, wholeR, wholeG, wholeB, wholeA, wholeW); - float maxg = this.Maximize(ref set1, 2, set1.G0 + 1, set1.G1, out int cutg, wholeR, wholeG, wholeB, wholeA, wholeW); - float maxb = this.Maximize(ref set1, 1, set1.B0 + 1, set1.B1, out int cutb, wholeR, wholeG, wholeB, wholeA, wholeW); - float maxa = this.Maximize(ref set1, 0, set1.A0 + 1, set1.A1, out int cuta, wholeR, wholeG, wholeB, wholeA, wholeW); + float maxr = this.Maximize(ref set1, 3, set1.RMin + 1, set1.RMax, out int cutr, wholeR, wholeG, wholeB, wholeA, wholeW); + float maxg = this.Maximize(ref set1, 2, set1.GMin + 1, set1.GMax, out int cutg, wholeR, wholeG, wholeB, wholeA, wholeW); + float maxb = this.Maximize(ref set1, 1, set1.BMin + 1, set1.BMax, out int cutb, wholeR, wholeG, wholeB, wholeA, wholeW); + float maxa = this.Maximize(ref set1, 0, set1.AMin + 1, set1.AMax, out int cuta, wholeR, wholeG, wholeB, wholeA, wholeW); int dir; @@ -743,48 +746,48 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization dir = 0; } - set2.R1 = set1.R1; - set2.G1 = set1.G1; - set2.B1 = set1.B1; - set2.A1 = set1.A1; + set2.RMax = set1.RMax; + set2.GMax = set1.GMax; + set2.BMax = set1.BMax; + set2.AMax = set1.AMax; switch (dir) { // Red case 3: - set2.R0 = set1.R1 = cutr; - set2.G0 = set1.G0; - set2.B0 = set1.B0; - set2.A0 = set1.A0; + set2.RMin = set1.RMax = cutr; + set2.GMin = set1.GMin; + set2.BMin = set1.BMin; + set2.AMin = set1.AMin; break; // Green case 2: - set2.G0 = set1.G1 = cutg; - set2.R0 = set1.R0; - set2.B0 = set1.B0; - set2.A0 = set1.A0; + set2.GMin = set1.GMax = cutg; + set2.RMin = set1.RMin; + set2.BMin = set1.BMin; + set2.AMin = set1.AMin; break; // Blue case 1: - set2.B0 = set1.B1 = cutb; - set2.R0 = set1.R0; - set2.G0 = set1.G0; - set2.A0 = set1.A0; + set2.BMin = set1.BMax = cutb; + set2.RMin = set1.RMin; + set2.GMin = set1.GMin; + set2.AMin = set1.AMin; break; // Alpha case 0: - set2.A0 = set1.A1 = cuta; - set2.R0 = set1.R0; - set2.G0 = set1.G0; - set2.B0 = set1.B0; + set2.AMin = set1.AMax = cuta; + set2.RMin = set1.RMin; + set2.GMin = set1.GMin; + set2.BMin = set1.BMin; break; } - set1.Volume = (set1.R1 - set1.R0) * (set1.G1 - set1.G0) * (set1.B1 - set1.B0) * (set1.A1 - set1.A0); - set2.Volume = (set2.R1 - set2.R0) * (set2.G1 - set2.G0) * (set2.B1 - set2.B0) * (set2.A1 - set2.A0); + set1.Volume = (set1.RMax - set1.RMin) * (set1.GMax - set1.GMin) * (set1.BMax - set1.BMin) * (set1.AMax - set1.AMin); + set2.Volume = (set2.RMax - set2.RMin) * (set2.GMax - set2.GMin) * (set2.BMax - set2.BMin) * (set2.AMax - set2.AMin); return true; } @@ -798,13 +801,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization { Span tagSpan = this.tag.GetSpan(); - for (int r = cube.R0 + 1; r <= cube.R1; r++) + for (int r = cube.RMin + 1; r <= cube.RMax; r++) { - for (int g = cube.G0 + 1; g <= cube.G1; g++) + for (int g = cube.GMin + 1; g <= cube.GMax; g++) { - for (int b = cube.B0 + 1; b <= cube.B1; b++) + for (int b = cube.BMin + 1; b <= cube.BMax; b++) { - for (int a = cube.A0 + 1; a <= cube.A1; a++) + for (int a = cube.AMin + 1; a <= cube.AMax; a++) { tagSpan[GetPaletteIndex(r, g, b, a)] = label; } @@ -819,12 +822,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization private void BuildCube() { this.colorCube = new Box[this.colors]; - float[] vv = new float[this.colors]; + double[] vv = new double[this.colors]; ref Box cube = ref this.colorCube[0]; - cube.R0 = cube.G0 = cube.B0 = cube.A0 = 0; - cube.R1 = cube.G1 = cube.B1 = IndexCount - 1; - cube.A1 = IndexAlphaCount - 1; + cube.RMin = cube.GMin = cube.BMin = cube.AMin = 0; + cube.RMax = cube.GMax = cube.BMax = IndexCount - 1; + cube.AMax = IndexAlphaCount - 1; int next = 0; @@ -839,13 +842,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization } else { - vv[next] = 0F; + vv[next] = 0D; i--; } next = 0; - float temp = vv[0]; + double temp = vv[0]; for (int k = 1; k <= i; k++) { if (vv[k] > temp) @@ -855,7 +858,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization } } - if (temp <= 0F) + if (temp <= 0D) { this.colors = i + 1; break; @@ -897,52 +900,83 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// /// Represents a box color cube. /// - private struct Box + private struct Box : IEquatable { /// /// Gets or sets the min red value, exclusive. /// - public int R0; + public int RMin; /// /// Gets or sets the max red value, inclusive. /// - public int R1; + public int RMax; /// /// Gets or sets the min green value, exclusive. /// - public int G0; + public int GMin; /// /// Gets or sets the max green value, inclusive. /// - public int G1; + public int GMax; /// /// Gets or sets the min blue value, exclusive. /// - public int B0; + public int BMin; /// /// Gets or sets the max blue value, inclusive. /// - public int B1; + public int BMax; /// /// Gets or sets the min alpha value, exclusive. /// - public int A0; + public int AMin; /// /// Gets or sets the max alpha value, inclusive. /// - public int A1; + public int AMax; /// /// Gets or sets the volume. /// public int Volume; + + /// + public override bool Equals(object obj) => obj is Box box && this.Equals(box); + + /// + public bool Equals(Box other) => + this.RMin == other.RMin + && this.RMax == other.RMax + && this.GMin == other.GMin + && this.GMax == other.GMax + && this.BMin == other.BMin + && this.BMax == other.BMax + && this.AMin == other.AMin + && this.AMax == other.AMax + && this.Volume == other.Volume; + + /// + public override int GetHashCode() + { + HashCode hash = default; + hash.Add(this.RMin); + hash.Add(this.RMax); + hash.Add(this.GMin); + hash.Add(this.GMax); + hash.Add(this.BMin); + hash.Add(this.BMax); + hash.Add(this.AMin); + hash.Add(this.AMax); + hash.Add(this.Volume); + return hash.ToHashCode(); + } } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Quantization/WuQuantizerTests.cs b/tests/ImageSharp.Tests/Quantization/WuQuantizerTests.cs new file mode 100644 index 0000000000..3eacd74ea1 --- /dev/null +++ b/tests/ImageSharp.Tests/Quantization/WuQuantizerTests.cs @@ -0,0 +1,168 @@ +using System; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors.Quantization; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Quantization +{ + public class WuQuantizerTests + { + [Fact] + public void SinglePixelOpaque() + { + Configuration config = Configuration.Default; + var quantizer = new WuQuantizer(false); + + using (var image = new Image(config, 1, 1, Rgba32.Black)) + using (QuantizedFrame result = quantizer.CreateFrameQuantizer(config).QuantizeFrame(image.Frames[0])) + { + Assert.Equal(1, result.Palette.Length); + Assert.Equal(1, result.GetPixelSpan().Length); + + Assert.Equal(Rgba32.Black, result.Palette[0]); + Assert.Equal(0, result.GetPixelSpan()[0]); + } + } + + [Fact] + public void SinglePixelTransparent() + { + Configuration config = Configuration.Default; + var quantizer = new WuQuantizer(false); + + using (var image = new Image(config, 1, 1, default(Rgba32))) + using (QuantizedFrame result = quantizer.CreateFrameQuantizer(config).QuantizeFrame(image.Frames[0])) + { + Assert.Equal(1, result.Palette.Length); + Assert.Equal(1, result.GetPixelSpan().Length); + + Assert.Equal(default, result.Palette[0]); + Assert.Equal(0, result.GetPixelSpan()[0]); + } + } + + [Fact] + public void GrayScale() => TestScale(c => new Rgba32(c, c, c, 128)); + + [Fact] + public void RedScale() => TestScale(c => new Rgba32(c, 0, 0, 128)); + + [Fact] + public void GreenScale() => TestScale(c => new Rgba32(0, c, 0, 128)); + + [Fact] + public void BlueScale() => TestScale(c => new Rgba32(0, 0, c, 128)); + + [Fact] + public void AlphaScale() => TestScale(c => new Rgba32(0, 0, 0, c)); + + [Fact] + public void Palette256() + { + using (var image = new Image(1, 256)) + { + for (int i = 0; i < 256; i++) + { + byte r = (byte)((i % 4) * 85); + byte g = (byte)(((i / 4) % 4) * 85); + byte b = (byte)(((i / 16) % 4) * 85); + byte a = (byte)((i / 64) * 85); + + image[0, i] = new Rgba32(r, g, b, a); + } + + Configuration config = Configuration.Default; + var quantizer = new WuQuantizer(false); + using (IFrameQuantizer frameQuantizer = quantizer.CreateFrameQuantizer(config)) + using (QuantizedFrame result = frameQuantizer.QuantizeFrame(image.Frames[0])) + { + Assert.Equal(256, result.Palette.Length); + Assert.Equal(256, result.GetPixelSpan().Length); + + var actualImage = new Image(1, 256); + + int paletteCount = result.Palette.Length - 1; + for (int y = 0; y < actualImage.Height; y++) + { + Span row = actualImage.GetPixelRowSpan(y); + ReadOnlySpan quantizedPixelSpan = result.GetPixelSpan(); + int yy = y * actualImage.Width; + + for (int x = 0; x < actualImage.Width; x++) + { + int i = x + yy; + row[x] = result.Palette[Math.Min(paletteCount, quantizedPixelSpan[i])]; + } + } + + Assert.True(image.GetPixelSpan().SequenceEqual(actualImage.GetPixelSpan())); + } + } + } + + [Theory] + [WithFile(TestImages.Png.LowColorVariance, PixelTypes.Rgba32)] + public void LowVariance(TestImageProvider provider) + where TPixel : struct, IPixel + { + // See https://github.com/SixLabors/ImageSharp/issues/866 + using (Image image = provider.GetImage()) + { + Configuration config = Configuration.Default; + var quantizer = new WuQuantizer(false); + using (IFrameQuantizer frameQuantizer = quantizer.CreateFrameQuantizer(config)) + using (QuantizedFrame result = frameQuantizer.QuantizeFrame(image.Frames[0])) + { + Assert.Equal(48, result.Palette.Length); + } + } + } + + private static void TestScale(Func pixelBuilder) + { + using (var image = new Image(1, 256)) + using (var expectedImage = new Image(1, 256)) + using (var actualImage = new Image(1, 256)) + { + for (int i = 0; i < 256; i++) + { + byte c = (byte)i; + image[0, i] = pixelBuilder.Invoke(c); + } + + for (int i = 0; i < 256; i++) + { + byte c = (byte)((i & ~7) + 4); + expectedImage[0, i] = pixelBuilder.Invoke(c); + } + + Configuration config = Configuration.Default; + var quantizer = new WuQuantizer(false); + + using (IFrameQuantizer frameQuantizer = quantizer.CreateFrameQuantizer(config)) + using (QuantizedFrame result = frameQuantizer.QuantizeFrame(image.Frames[0])) + { + Assert.Equal(4 * 8, result.Palette.Length); + Assert.Equal(256, result.GetPixelSpan().Length); + + int paletteCount = result.Palette.Length - 1; + for (int y = 0; y < actualImage.Height; y++) + { + Span row = actualImage.GetPixelRowSpan(y); + ReadOnlySpan quantizedPixelSpan = result.GetPixelSpan(); + int yy = y * actualImage.Width; + + for (int x = 0; x < actualImage.Width; x++) + { + int i = x + yy; + row[x] = result.Palette[Math.Min(paletteCount, quantizedPixelSpan[i])]; + } + } + } + + Assert.True(expectedImage.GetPixelSpan().SequenceEqual(actualImage.GetPixelSpan())); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 9099696247..1efb9e35fd 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -53,6 +53,7 @@ namespace SixLabors.ImageSharp.Tests public const string Gray2BitTrans = "Png/gray-2-tRNS.png"; public const string Gray4BitTrans = "Png/gray-4-tRNS.png"; public const string Gray8BitTrans = "Png/gray-8-tRNS.png"; + public const string LowColorVariance = "Png/low-variance.png"; // Filtered test images from http://www.schaik.com/pngsuite/pngsuite_fil_png.html public const string Filter0 = "Png/filter0.png"; diff --git a/tests/Images/Input/Png/low-variance.png b/tests/Images/Input/Png/low-variance.png new file mode 100644 index 0000000000..7d8b1043eb --- /dev/null +++ b/tests/Images/Input/Png/low-variance.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:70c224ef674b546db0ec3281008bb6e2b879e95b0be5625f0af8aff980eee583 +size 7844 From d645b5599e43ea1f5106ec506b1d88b90f4ed55a Mon Sep 17 00:00:00 2001 From: Jason Nelson Date: Sat, 13 Apr 2019 21:01:04 -0500 Subject: [PATCH 056/223] Nits - Benchmarks (#884) * Update metadata names * Use WithIterationCount * Format Benchmark documents * Update copyright assignment to Six Labors & Contributors * Update deps * React to Benchmark library update --- tests/ImageSharp.Benchmarks/BenchmarkBase.cs | 9 ++-- .../ImageSharp.Benchmarks/Codecs/DecodeBmp.cs | 2 +- .../Codecs/DecodeFilteredPng.cs | 4 +- .../ImageSharp.Benchmarks/Codecs/DecodePng.cs | 1 - .../Codecs/ImageBenchmarkTests.cs | 4 +- .../BlockOperations/Block8x8F_CopyTo2x2.cs | 40 +++++++++------ .../Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs | 8 +-- .../Codecs/Jpeg/EncodeJpeg.cs | 18 +++---- .../Codecs/MultiImageBenchmarkBase.cs | 18 +++---- .../Color/ColorEquality.cs | 12 ++--- .../Color/ColorspaceCieXyzToCieLabConvert.cs | 14 ++--- .../ColorspaceCieXyzToHunterLabConvert.cs | 15 +++--- .../Color/ColorspaceCieXyzToLmsConvert.cs | 15 +++--- .../Color/ColorspaceCieXyzToRgbConvert.cs | 14 ++--- .../ImageSharp.Benchmarks/Color/RgbToYCbCr.cs | 32 ++++++------ .../Color/RgbWorkingSpaceAdapt.cs | 15 +++--- tests/ImageSharp.Benchmarks/Config.cs | 17 +++---- .../Drawing/DrawBeziers.cs | 38 +++++++------- .../Drawing/DrawLines.cs | 51 +++++++++---------- .../Drawing/DrawPolygon.cs | 35 ++++++------- .../ImageSharp.Benchmarks/Drawing/DrawText.cs | 26 ++++------ .../Drawing/DrawTextOutline.cs | 32 +++++------- .../Drawing/FillPolygon.cs | 49 +++++++++--------- .../Drawing/FillRectangle.cs | 24 ++++----- .../Drawing/FillWithPattern.cs | 28 +++++----- .../ImageSharp.Benchmarks/General/Array2D.cs | 14 +++-- .../General/ArrayCopy.cs | 19 ++++--- .../General/ArrayReverse.cs | 14 +++-- .../General/BasicMath/ClampInt32IntoByte.cs | 4 +- .../PixelConversion_ConvertToRgba32.cs | 5 +- .../PixelConversion_ConvertToVector4.cs | 2 - .../General/Vector4Constants.cs | 10 ++-- .../General/Vectorization/BitwiseOrUint32.cs | 8 +-- .../General/Vectorization/DivFloat.cs | 12 ++--- .../General/Vectorization/DivUInt32.cs | 14 ++--- .../General/Vectorization/Divide.cs | 9 ++-- .../General/Vectorization/MulFloat.cs | 8 +-- .../General/Vectorization/MulUInt32.cs | 10 ++-- .../General/Vectorization/Multiply.cs | 6 +-- .../Vectorization/ReinterpretUInt32AsFloat.cs | 12 ++--- .../Vectorization/SIMDBenchmarkBase.cs | 11 ++-- .../ImageSharp.Benchmarks.csproj | 6 +-- tests/ImageSharp.Benchmarks/Program.cs | 13 ++--- tests/ImageSharp.Benchmarks/Samplers/Crop.cs | 42 +++++++-------- .../Samplers/DetectEdges.cs | 6 +-- tests/ImageSharp.Benchmarks/Samplers/Glow.cs | 13 +++-- .../ImageSharp.Benchmarks/Samplers/Resize.cs | 1 - .../ImageSharp.Benchmarks/Samplers/Rotate.cs | 5 +- tests/ImageSharp.Benchmarks/Samplers/Skew.cs | 5 +- 49 files changed, 352 insertions(+), 418 deletions(-) diff --git a/tests/ImageSharp.Benchmarks/BenchmarkBase.cs b/tests/ImageSharp.Benchmarks/BenchmarkBase.cs index 6db03a4486..87ed8fa423 100644 --- a/tests/ImageSharp.Benchmarks/BenchmarkBase.cs +++ b/tests/ImageSharp.Benchmarks/BenchmarkBase.cs @@ -1,7 +1,8 @@ -namespace SixLabors.ImageSharp.Benchmarks -{ - using SixLabors.ImageSharp.Formats; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. +namespace SixLabors.ImageSharp.Benchmarks +{ /// /// The image benchmark base class. /// @@ -15,4 +16,4 @@ // Add Image Formats } } -} +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Codecs/DecodeBmp.cs b/tests/ImageSharp.Benchmarks/Codecs/DecodeBmp.cs index 30799aabf9..1ab5ed3099 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/DecodeBmp.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/DecodeBmp.cs @@ -54,4 +54,4 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs } } } -} +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Codecs/DecodeFilteredPng.cs b/tests/ImageSharp.Benchmarks/Codecs/DecodeFilteredPng.cs index ff378c75c3..cc946e05ad 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/DecodeFilteredPng.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/DecodeFilteredPng.cs @@ -1,7 +1,5 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// using System.IO; using System.Runtime.CompilerServices; diff --git a/tests/ImageSharp.Benchmarks/Codecs/DecodePng.cs b/tests/ImageSharp.Benchmarks/Codecs/DecodePng.cs index 39f09b6b6f..a19d8fa91e 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/DecodePng.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/DecodePng.cs @@ -11,7 +11,6 @@ using SDImage = System.Drawing.Image; namespace SixLabors.ImageSharp.Benchmarks.Codecs { - [Config(typeof(Config.ShortClr))] public class DecodePng : BenchmarkBase { diff --git a/tests/ImageSharp.Benchmarks/Codecs/ImageBenchmarkTests.cs b/tests/ImageSharp.Benchmarks/Codecs/ImageBenchmarkTests.cs index ee77a2b6b4..7c3da90db6 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/ImageBenchmarkTests.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/ImageBenchmarkTests.cs @@ -1,7 +1,5 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// // This file contains small, cheap and "unit test" benchmarks to test MultiImageBenchmarkBase. // Need this because there are no real test cases for the common benchmark utility stuff. diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo2x2.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo2x2.cs index 65176af5bb..3d9b54dffa 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo2x2.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo2x2.cs @@ -364,22 +364,30 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations ref Vector4 dTopLeft = ref Unsafe.As(ref Unsafe.Add(ref destBase, offset)); ref Vector4 dBottomLeft = ref Unsafe.As(ref Unsafe.Add(ref destBase, offset + destStride)); - var xyLeft = new Vector4(sLeft.X); - xyLeft.Z = sLeft.Y; - xyLeft.W = sLeft.Y; - - var zwLeft = new Vector4(sLeft.Z); - zwLeft.Z = sLeft.W; - zwLeft.W = sLeft.W; - - var xyRight = new Vector4(sRight.X); - xyRight.Z = sRight.Y; - xyRight.W = sRight.Y; - - var zwRight = new Vector4(sRight.Z); - zwRight.Z = sRight.W; - zwRight.W = sRight.W; - + var xyLeft = new Vector4(sLeft.X) + { + Z = sLeft.Y, + W = sLeft.Y + }; + + var zwLeft = new Vector4(sLeft.Z) + { + Z = sLeft.W, + W = sLeft.W + }; + + var xyRight = new Vector4(sRight.X) + { + Z = sRight.Y, + W = sRight.Y + }; + + var zwRight = new Vector4(sRight.Z) + { + Z = sRight.W, + W = sRight.W + }; + dTopLeft = xyLeft; Unsafe.Add(ref dTopLeft, 1) = zwLeft; Unsafe.Add(ref dTopLeft, 2) = xyRight; diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs index fe112042ef..8247ba42b5 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs @@ -5,6 +5,7 @@ using System.Drawing; using System.IO; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Diagnosers; using BenchmarkDotNet.Jobs; using SixLabors.ImageSharp.Formats.Jpeg; @@ -26,8 +27,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg { public Config() { - // Uncomment if you want to use any of the diagnoser - this.Add(new BenchmarkDotNet.Diagnosers.MemoryDiagnoser()); + this.Add(MemoryDiagnoser.Default); } public class ShortClr : Benchmarks.Config @@ -35,8 +35,8 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg public ShortClr() { this.Add( - //Job.Clr.WithLaunchCount(1).WithWarmupCount(2).WithTargetCount(3), - Job.Core.WithLaunchCount(1).WithWarmupCount(2).WithTargetCount(3) + //Job.Clr.WithLaunchCount(1).WithWarmupCount(2).WithIterationCount(3), + Job.Core.WithLaunchCount(1).WithWarmupCount(2).WithIterationCount(3) ); } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs index d934a1d6de..c617d25c07 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs @@ -1,18 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// using SixLabors.ImageSharp.PixelFormats; +using BenchmarkDotNet.Attributes; + namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg { using System.Drawing; using System.Drawing.Imaging; using System.IO; - using BenchmarkDotNet.Attributes; - using CoreImage = SixLabors.ImageSharp.Image; public class EncodeJpeg : BenchmarkBase @@ -45,19 +43,19 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg [Benchmark(Baseline = true, Description = "System.Drawing Jpeg")] public void JpegSystemDrawing() { - using (var memoryStream = new MemoryStream()) + using (var stream = new MemoryStream()) { - this.bmpDrawing.Save(memoryStream, ImageFormat.Jpeg); + this.bmpDrawing.Save(stream, ImageFormat.Jpeg); } } [Benchmark(Description = "ImageSharp Jpeg")] public void JpegCore() { - using (var memoryStream = new MemoryStream()) + using (var stream = new MemoryStream()) { - this.bmpCore.SaveAsJpeg(memoryStream); + this.bmpCore.SaveAsJpeg(stream); } } } -} +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Codecs/MultiImageBenchmarkBase.cs b/tests/ImageSharp.Benchmarks/Codecs/MultiImageBenchmarkBase.cs index 446c038596..75bbf21a33 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/MultiImageBenchmarkBase.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/MultiImageBenchmarkBase.cs @@ -1,7 +1,5 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// using BenchmarkDotNet.Configs; using BenchmarkDotNet.Jobs; @@ -18,7 +16,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs using System.Numerics; using BenchmarkDotNet.Attributes; - + using BenchmarkDotNet.Diagnosers; using SixLabors.ImageSharp.Tests; using CoreImage = ImageSharp.Image; @@ -30,7 +28,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs public Config() { // Uncomment if you want to use any of the diagnoser - this.Add(new BenchmarkDotNet.Diagnosers.MemoryDiagnoser()); + this.Add(MemoryDiagnoser.Default); } public class ShortClr : Benchmarks.Config @@ -38,7 +36,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs public ShortClr() { this.Add( - Job.Core.WithLaunchCount(1).WithWarmupCount(1).WithTargetCount(2) + Job.Core.WithLaunchCount(1).WithWarmupCount(1).WithIterationCount(2) ); } } @@ -47,7 +45,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs protected Dictionary FileNamesToBytes = new Dictionary(); protected Dictionary> FileNamesToImageSharpImages = new Dictionary>(); - protected Dictionary FileNamesToSystemDrawingImages = new Dictionary(); + protected Dictionary FileNamesToSystemDrawingImages = new Dictionary(); /// /// The values of this enum separate input files into categories @@ -152,7 +150,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs { foreach (KeyValuePair kv in this.FileNames2Bytes) { - using (MemoryStream memoryStream = new MemoryStream(kv.Value)) + using (var memoryStream = new MemoryStream(kv.Value)) { try { @@ -179,7 +177,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs byte[] bytes = kv.Value; string fn = kv.Key; - using (MemoryStream ms1 = new MemoryStream(bytes)) + using (var ms1 = new MemoryStream(bytes)) { this.FileNamesToImageSharpImages[fn] = CoreImage.Load(ms1); @@ -223,7 +221,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs protected void ForEachImageSharpImage(Func, MemoryStream, object> operation) { - using (MemoryStream workStream = new MemoryStream()) + using (var workStream = new MemoryStream()) { this.ForEachImageSharpImage( diff --git a/tests/ImageSharp.Benchmarks/Color/ColorEquality.cs b/tests/ImageSharp.Benchmarks/Color/ColorEquality.cs index 02017cbb7d..602e1137f0 100644 --- a/tests/ImageSharp.Benchmarks/Color/ColorEquality.cs +++ b/tests/ImageSharp.Benchmarks/Color/ColorEquality.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace SixLabors.ImageSharp.Benchmarks -{ - using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Attributes; - using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Benchmarks +{ using SystemColor = System.Drawing.Color; public class ColorEquality diff --git a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToCieLabConvert.cs b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToCieLabConvert.cs index cc3472e222..855f5b9b40 100644 --- a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToCieLabConvert.cs +++ b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToCieLabConvert.cs @@ -1,13 +1,13 @@ -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces -{ - using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Attributes; - using Colourful; - using Colourful.Conversion; +using Colourful; +using Colourful.Conversion; - using SixLabors.ImageSharp.ColorSpaces; - using SixLabors.ImageSharp.ColorSpaces.Conversion; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces +{ public class ColorspaceCieXyzToCieLabConvert { private static readonly CieXyz CieXyz = new CieXyz(0.95047F, 1, 1.08883F); diff --git a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToHunterLabConvert.cs b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToHunterLabConvert.cs index d109995184..07870b3a85 100644 --- a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToHunterLabConvert.cs +++ b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToHunterLabConvert.cs @@ -1,13 +1,13 @@ -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces -{ - using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Attributes; - using Colourful; - using Colourful.Conversion; +using Colourful; +using Colourful.Conversion; - using SixLabors.ImageSharp.ColorSpaces; - using SixLabors.ImageSharp.ColorSpaces.Conversion; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces +{ public class ColorspaceCieXyzToHunterLabConvert { private static readonly CieXyz CieXyz = new CieXyz(0.95047F, 1, 1.08883F); @@ -18,7 +18,6 @@ private static readonly ColourfulConverter ColourfulConverter = new ColourfulConverter(); - [Benchmark(Baseline = true, Description = "Colourful Convert")] public double ColourfulConvert() { diff --git a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToLmsConvert.cs b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToLmsConvert.cs index da7b9c3dd3..4d9ba89286 100644 --- a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToLmsConvert.cs +++ b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToLmsConvert.cs @@ -1,13 +1,13 @@ -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces -{ - using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Attributes; - using Colourful; - using Colourful.Conversion; +using Colourful; +using Colourful.Conversion; - using SixLabors.ImageSharp.ColorSpaces; - using SixLabors.ImageSharp.ColorSpaces.Conversion; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces +{ public class ColorspaceCieXyzToLmsConvert { private static readonly CieXyz CieXyz = new CieXyz(0.95047F, 1, 1.08883F); @@ -18,7 +18,6 @@ private static readonly ColourfulConverter ColourfulConverter = new ColourfulConverter(); - [Benchmark(Baseline = true, Description = "Colourful Convert")] public double ColourfulConvert() { diff --git a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToRgbConvert.cs b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToRgbConvert.cs index 2a5754ab0e..f20ffdcabc 100644 --- a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToRgbConvert.cs +++ b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToRgbConvert.cs @@ -1,13 +1,13 @@ -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces -{ - using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Attributes; - using Colourful; - using Colourful.Conversion; +using Colourful; +using Colourful.Conversion; - using SixLabors.ImageSharp.ColorSpaces; - using SixLabors.ImageSharp.ColorSpaces.Conversion; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces +{ public class ColorspaceCieXyzToRgbConvert { private static readonly CieXyz CieXyz = new CieXyz(0.95047F, 1, 1.08883F); diff --git a/tests/ImageSharp.Benchmarks/Color/RgbToYCbCr.cs b/tests/ImageSharp.Benchmarks/Color/RgbToYCbCr.cs index 07ae17d754..b4e5ab3802 100644 --- a/tests/ImageSharp.Benchmarks/Color/RgbToYCbCr.cs +++ b/tests/ImageSharp.Benchmarks/Color/RgbToYCbCr.cs @@ -136,7 +136,7 @@ namespace SixLabors.ImageSharp.Benchmarks OnStackInputCache.Byte input = OnStackInputCache.Byte.Create(this.inputSourceRGB); // On-stack output: - Result result = default(Result); + Result result = default; float* yPtr = (float*)&result.Y; float* cbPtr = (float*)&result.Cb; float* crPtr = (float*)&result.Cr; @@ -162,7 +162,7 @@ namespace SixLabors.ImageSharp.Benchmarks OnStackInputCache.Byte input = OnStackInputCache.Byte.Create(this.inputSourceRGB); // On-stack output: - Result result = default(Result); + Result result = default; float* yPtr = (float*)&result.Y; float* cbPtr = (float*)&result.Cb; float* crPtr = (float*)&result.Cr; @@ -194,15 +194,15 @@ namespace SixLabors.ImageSharp.Benchmarks // Copy the input to the stack: // On-stack output: - Result result = default(Result); + Result result = default; float* yPtr = (float*)&result.Y; float* cbPtr = (float*)&result.Cb; float* crPtr = (float*)&result.Cr; // end of code-bloat block :) - Vector yCoeffs = new Vector(ScaledCoeffs.Y); - Vector cbCoeffs = new Vector(ScaledCoeffs.Cb); - Vector crCoeffs = new Vector(ScaledCoeffs.Cr); + var yCoeffs = new Vector(ScaledCoeffs.Y); + var cbCoeffs = new Vector(ScaledCoeffs.Cb); + var crCoeffs = new Vector(ScaledCoeffs.Cr); for (int i = 0; i < this.inputSourceRGB.Length; i++) { @@ -240,23 +240,23 @@ namespace SixLabors.ImageSharp.Benchmarks // Copy the input to the stack: // On-stack output: - Result result = default(Result); + Result result = default; float* yPtr = (float*)&result.Y; float* cbPtr = (float*)&result.Cb; float* crPtr = (float*)&result.Cr; // end of code-bloat block :) - Vector yCoeffs = new Vector(ScaledCoeffs.Y); - Vector cbCoeffs = new Vector(ScaledCoeffs.Cb); - Vector crCoeffs = new Vector(ScaledCoeffs.Cr); + var yCoeffs = new Vector(ScaledCoeffs.Y); + var cbCoeffs = new Vector(ScaledCoeffs.Cb); + var crCoeffs = new Vector(ScaledCoeffs.Cr); - Vector leftY = new Vector(ScaledCoeffs.SelectLeft.Y); - Vector leftCb = new Vector(ScaledCoeffs.SelectLeft.Cb); - Vector leftCr = new Vector(ScaledCoeffs.SelectLeft.Cr); + var leftY = new Vector(ScaledCoeffs.SelectLeft.Y); + var leftCb = new Vector(ScaledCoeffs.SelectLeft.Cb); + var leftCr = new Vector(ScaledCoeffs.SelectLeft.Cr); - Vector rightY = new Vector(ScaledCoeffs.SelectRight.Y); - Vector rightCb = new Vector(ScaledCoeffs.SelectRight.Cb); - Vector rightCr = new Vector(ScaledCoeffs.SelectRight.Cr); + var rightY = new Vector(ScaledCoeffs.SelectRight.Y); + var rightCb = new Vector(ScaledCoeffs.SelectRight.Cb); + var rightCr = new Vector(ScaledCoeffs.SelectRight.Cr); for (int i = 0; i < this.inputSourceRGB.Length; i++) { diff --git a/tests/ImageSharp.Benchmarks/Color/RgbWorkingSpaceAdapt.cs b/tests/ImageSharp.Benchmarks/Color/RgbWorkingSpaceAdapt.cs index 92008f6e20..060a28550e 100644 --- a/tests/ImageSharp.Benchmarks/Color/RgbWorkingSpaceAdapt.cs +++ b/tests/ImageSharp.Benchmarks/Color/RgbWorkingSpaceAdapt.cs @@ -1,13 +1,13 @@ -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces -{ - using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Attributes; - using Colourful; - using Colourful.Conversion; +using Colourful; +using Colourful.Conversion; - using SixLabors.ImageSharp.ColorSpaces; - using SixLabors.ImageSharp.ColorSpaces.Conversion; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces +{ public class RgbWorkingSpaceAdapt { private static readonly Rgb Rgb = new Rgb(0.206162F, 0.260277F, 0.746717F, RgbWorkingSpaces.WideGamutRgb); @@ -18,7 +18,6 @@ private static readonly ColourfulConverter ColourfulConverter = new ColourfulConverter { TargetRGBWorkingSpace = RGBWorkingSpaces.sRGB }; - [Benchmark(Baseline = true, Description = "Colourful Adapt")] public RGBColor ColourfulConvert() { diff --git a/tests/ImageSharp.Benchmarks/Config.cs b/tests/ImageSharp.Benchmarks/Config.cs index b467579425..0543cbc50d 100644 --- a/tests/ImageSharp.Benchmarks/Config.cs +++ b/tests/ImageSharp.Benchmarks/Config.cs @@ -1,20 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Diagnosers; +using BenchmarkDotNet.Jobs; namespace SixLabors.ImageSharp.Benchmarks { - using BenchmarkDotNet.Jobs; - public class Config : ManualConfig { public Config() { - // Uncomment if you want to use any of the diagnoser - this.Add(new BenchmarkDotNet.Diagnosers.MemoryDiagnoser()); + this.Add(MemoryDiagnoser.Default); } public class ShortClr : Config @@ -22,9 +19,9 @@ namespace SixLabors.ImageSharp.Benchmarks public ShortClr() { this.Add( - Job.Clr.WithLaunchCount(1).WithWarmupCount(3).WithTargetCount(3), - Job.Core.WithLaunchCount(1).WithWarmupCount(3).WithTargetCount(3) - ); + Job.Clr.WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(3), + Job.Core.WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(3) + ); } } } diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs index 34dd9d4dc8..bc9c1c96d2 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs @@ -1,7 +1,5 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// using System.Drawing; using System.Drawing.Drawing2D; @@ -19,27 +17,25 @@ namespace SixLabors.ImageSharp.Benchmarks [Benchmark(Baseline = true, Description = "System.Drawing Draw Beziers")] public void DrawPathSystemDrawing() { - using (Bitmap destination = new Bitmap(800, 800)) + using (var destination = new Bitmap(800, 800)) + using (var graphics = Graphics.FromImage(destination)) { + graphics.InterpolationMode = InterpolationMode.Default; + graphics.SmoothingMode = SmoothingMode.AntiAlias; - using (Graphics graphics = Graphics.FromImage(destination)) + using (var pen = new Pen(Color.HotPink, 10)) { - graphics.InterpolationMode = InterpolationMode.Default; - graphics.SmoothingMode = SmoothingMode.AntiAlias; - using (var pen = new Pen(System.Drawing.Color.HotPink, 10)) - { - graphics.DrawBeziers(pen, new[] { - new PointF(10, 500), - new PointF(30, 10), - new PointF(240, 30), - new PointF(300, 500) - }); - } + graphics.DrawBeziers(pen, new[] { + new PointF(10, 500), + new PointF(30, 10), + new PointF(240, 30), + new PointF(300, 500) + }); } - using (MemoryStream ms = new MemoryStream()) + using (var stream = new MemoryStream()) { - destination.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp); + destination.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp); } } } @@ -47,7 +43,7 @@ namespace SixLabors.ImageSharp.Benchmarks [Benchmark(Description = "ImageSharp Draw Beziers")] public void DrawLinesCore() { - using (Image image = new Image(800, 800)) + using (var image = new Image(800, 800)) { image.Mutate(x => x.DrawBeziers( Rgba32.HotPink, @@ -59,9 +55,9 @@ namespace SixLabors.ImageSharp.Benchmarks new Vector2(300, 500) })); - using (MemoryStream ms = new MemoryStream()) + using (var stream = new MemoryStream()) { - image.SaveAsBmp(ms); + image.SaveAsBmp(stream); } } } diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs index 091f367622..4265525e5b 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs @@ -1,44 +1,41 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace SixLabors.ImageSharp.Benchmarks -{ - using System.Drawing; - using System.Drawing.Drawing2D; - using System.IO; - using System.Numerics; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.IO; +using System.Numerics; - using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Attributes; - using SixLabors.ImageSharp.PixelFormats; - using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +namespace SixLabors.ImageSharp.Benchmarks +{ public class DrawLines : BenchmarkBase { [Benchmark(Baseline = true, Description = "System.Drawing Draw Lines")] public void DrawPathSystemDrawing() { using (var destination = new Bitmap(800, 800)) + using (var graphics = Graphics.FromImage(destination)) { - using (var graphics = Graphics.FromImage(destination)) + graphics.InterpolationMode = InterpolationMode.Default; + graphics.SmoothingMode = SmoothingMode.AntiAlias; + + using (var pen = new Pen(Color.HotPink, 10)) { - graphics.InterpolationMode = InterpolationMode.Default; - graphics.SmoothingMode = SmoothingMode.AntiAlias; - using (var pen = new Pen(System.Drawing.Color.HotPink, 10)) - { - graphics.DrawLines(pen, new[] { - new PointF(10, 10), - new PointF(550, 50), - new PointF(200, 400) - }); - } + graphics.DrawLines(pen, new[] { + new PointF(10, 10), + new PointF(550, 50), + new PointF(200, 400) + }); } - using (var ms = new MemoryStream()) + using (var stream = new MemoryStream()) { - destination.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp); + destination.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp); } } } @@ -57,9 +54,9 @@ namespace SixLabors.ImageSharp.Benchmarks new Vector2(200, 400) })); - using (var ms = new MemoryStream()) + using (var stream = new MemoryStream()) { - image.SaveAsBmp(ms); + image.SaveAsBmp(stream); } } } diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs index ab8a7134e7..4172b3c38f 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs @@ -1,7 +1,5 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// using System.Drawing; using System.Drawing.Drawing2D; @@ -19,26 +17,23 @@ namespace SixLabors.ImageSharp.Benchmarks [Benchmark(Baseline = true, Description = "System.Drawing Draw Polygon")] public void DrawPolygonSystemDrawing() { - using (Bitmap destination = new Bitmap(800, 800)) + using (var destination = new Bitmap(800, 800)) + using (var graphics = Graphics.FromImage(destination)) { - - using (Graphics graphics = Graphics.FromImage(destination)) + graphics.InterpolationMode = InterpolationMode.Default; + graphics.SmoothingMode = SmoothingMode.AntiAlias; + using (var pen = new Pen(Color.HotPink, 10)) { - graphics.InterpolationMode = InterpolationMode.Default; - graphics.SmoothingMode = SmoothingMode.AntiAlias; - using (var pen = new Pen(System.Drawing.Color.HotPink, 10)) - { - graphics.DrawPolygon(pen, new[] { - new PointF(10, 10), - new PointF(550, 50), - new PointF(200, 400) - }); - } + graphics.DrawPolygon(pen, new[] { + new PointF(10, 10), + new PointF(550, 50), + new PointF(200, 400) + }); } - using (MemoryStream ms = new MemoryStream()) + using (var stream = new MemoryStream()) { - destination.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp); + destination.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp); } } } @@ -46,7 +41,7 @@ namespace SixLabors.ImageSharp.Benchmarks [Benchmark(Description = "ImageSharp Draw Polygon")] public void DrawPolygonCore() { - using (Image image = new Image(800, 800)) + using (var image = new Image(800, 800)) { image.Mutate(x => x.DrawPolygon( Rgba32.HotPink, @@ -57,7 +52,7 @@ namespace SixLabors.ImageSharp.Benchmarks new Vector2(200, 400) })); - using (MemoryStream ms = new MemoryStream()) + using (var ms = new MemoryStream()) { image.SaveAsBmp(ms); } diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawText.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawText.cs index 60c58dadc9..8c840d8c31 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/DrawText.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/DrawText.cs @@ -1,7 +1,5 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// using System.Drawing; using System.Drawing.Drawing2D; @@ -14,11 +12,9 @@ using SixLabors.ImageSharp.Processing.Processors.Text; namespace SixLabors.ImageSharp.Benchmarks { - [MemoryDiagnoser] public class DrawText : BenchmarkBase { - [Params(10, 100)] public int TextIterations { get; set; } public string TextPhrase { get; set; } = "Hello World"; @@ -28,26 +24,22 @@ namespace SixLabors.ImageSharp.Benchmarks [Benchmark(Baseline = true, Description = "System.Drawing Draw Text")] public void DrawTextSystemDrawing() { - using (Bitmap destination = new Bitmap(800, 800)) + using (var destination = new Bitmap(800, 800)) + using (var graphics = Graphics.FromImage(destination)) { - - using (Graphics graphics = Graphics.FromImage(destination)) + graphics.InterpolationMode = InterpolationMode.Default; + graphics.SmoothingMode = SmoothingMode.AntiAlias; + using (var font = new Font("Arial", 12, GraphicsUnit.Point)) { - graphics.InterpolationMode = InterpolationMode.Default; - graphics.SmoothingMode = SmoothingMode.AntiAlias; - using (var font = new Font("Arial", 12, GraphicsUnit.Point)) - { - graphics.DrawString(TextToRender, font, System.Drawing.Brushes.HotPink, new RectangleF(10, 10, 780, 780)); - } + graphics.DrawString(TextToRender, font, System.Drawing.Brushes.HotPink, new RectangleF(10, 10, 780, 780)); } } } - [Benchmark(Description = "ImageSharp Draw Text - Cached Glyphs")] public void DrawTextCore() { - using (Image image = new Image(800, 800)) + using (var image = new Image(800, 800)) { var font = SixLabors.Fonts.SystemFonts.CreateFont("Arial", 12); image.Mutate(x => x.ApplyProcessor(new DrawTextProcessor(new TextGraphicsOptions(true) { WrapTextWidth = 780 }, TextToRender, font, Processing.Brushes.Solid(Rgba32.HotPink), null, new SixLabors.Primitives.PointF(10, 10)))); @@ -57,7 +49,7 @@ namespace SixLabors.ImageSharp.Benchmarks [Benchmark(Description = "ImageSharp Draw Text - Nieve")] public void DrawTextCoreOld() { - using (Image image = new Image(800, 800)) + using (var image = new Image(800, 800)) { var font = SixLabors.Fonts.SystemFonts.CreateFont("Arial", 12); image.Mutate(x => DrawTextOldVersion(x, new TextGraphicsOptions(true) { WrapTextWidth = 780 }, TextToRender, font, Processing.Brushes.Solid(Rgba32.HotPink), null, new SixLabors.Primitives.PointF(10, 10))); diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawTextOutline.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawTextOutline.cs index 2d6505a91f..b99c47960b 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/DrawTextOutline.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/DrawTextOutline.cs @@ -1,7 +1,5 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// using System.Drawing; using System.Drawing.Drawing2D; @@ -13,34 +11,28 @@ using SixLabors.ImageSharp.Processing.Processors.Text; namespace SixLabors.ImageSharp.Benchmarks { - [MemoryDiagnoser] public class DrawTextOutline : BenchmarkBase { - [Params(10, 100)] public int TextIterations { get; set; } public string TextPhrase { get; set; } = "Hello World"; public string TextToRender => string.Join(" ", Enumerable.Repeat(TextPhrase, TextIterations)); - [Benchmark(Baseline = true, Description = "System.Drawing Draw Text Outline")] public void DrawTextSystemDrawing() { - using (Bitmap destination = new Bitmap(800, 800)) + using (var destination = new Bitmap(800, 800)) + using (var graphics = Graphics.FromImage(destination)) { - - using (Graphics graphics = Graphics.FromImage(destination)) + graphics.InterpolationMode = InterpolationMode.Default; + graphics.SmoothingMode = SmoothingMode.AntiAlias; + using (var pen = new Pen(Color.HotPink, 10)) + using (var font = new Font("Arial", 12, GraphicsUnit.Point)) + using (var gp = new GraphicsPath()) { - graphics.InterpolationMode = InterpolationMode.Default; - graphics.SmoothingMode = SmoothingMode.AntiAlias; - using (var pen = new Pen(System.Drawing.Color.HotPink, 10)) - using (var font = new Font("Arial", 12, GraphicsUnit.Point)) - using (var gp = new GraphicsPath()) - { - gp.AddString(TextToRender, font.FontFamily, (int)font.Style, font.Size, new RectangleF(10, 10, 780, 780), new StringFormat()); - graphics.DrawPath(pen, gp); - } + gp.AddString(TextToRender, font.FontFamily, (int)font.Style, font.Size, new RectangleF(10, 10, 780, 780), new StringFormat()); + graphics.DrawPath(pen, gp); } } } @@ -48,7 +40,7 @@ namespace SixLabors.ImageSharp.Benchmarks [Benchmark(Description = "ImageSharp Draw Text Outline - Cached Glyphs")] public void DrawTextCore() { - using (Image image = new Image(800, 800)) + using (var image = new Image(800, 800)) { var font = SixLabors.Fonts.SystemFonts.CreateFont("Arial", 12); image.Mutate(x => x.ApplyProcessor(new DrawTextProcessor(new TextGraphicsOptions(true) { WrapTextWidth = 780 }, TextToRender, font, null, Processing.Pens.Solid(Rgba32.HotPink, 10), new SixLabors.Primitives.PointF(10, 10)))); @@ -58,7 +50,7 @@ namespace SixLabors.ImageSharp.Benchmarks [Benchmark(Description = "ImageSharp Draw Text Outline - Nieve")] public void DrawTextCoreOld() { - using (Image image = new Image(800, 800)) + using (var image = new Image(800, 800)) { var font = SixLabors.Fonts.SystemFonts.CreateFont("Arial", 12); image.Mutate(x => DrawTextOldVersion(x, new TextGraphicsOptions(true) { WrapTextWidth = 780 }, TextToRender, font, null, Processing.Pens.Solid(Rgba32.HotPink, 10), new SixLabors.Primitives.PointF(10, 10))); diff --git a/tests/ImageSharp.Benchmarks/Drawing/FillPolygon.cs b/tests/ImageSharp.Benchmarks/Drawing/FillPolygon.cs index 8aadb85bf3..396cc18d10 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/FillPolygon.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/FillPolygon.cs @@ -1,7 +1,5 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// using System.Drawing; using System.Drawing.Drawing2D; @@ -21,31 +19,30 @@ namespace SixLabors.ImageSharp.Benchmarks public FillPolygon() { - this.shape = new Polygon(new LinearLineSegment(new Vector2(10, 10), - new Vector2(550, 50), - new Vector2(200, 400))); + this.shape = new Polygon(new LinearLineSegment( + new Vector2(10, 10), + new Vector2(550, 50), + new Vector2(200, 400))); } [Benchmark(Baseline = true, Description = "System.Drawing Fill Polygon")] public void DrawSolidPolygonSystemDrawing() { - using (Bitmap destination = new Bitmap(800, 800)) + using (var destination = new Bitmap(800, 800)) + + using (var graphics = Graphics.FromImage(destination)) { + graphics.SmoothingMode = SmoothingMode.AntiAlias; + graphics.FillPolygon(System.Drawing.Brushes.HotPink, + new[] { + new Point(10, 10), + new Point(550, 50), + new Point(200, 400) + }); - using (Graphics graphics = Graphics.FromImage(destination)) - { - graphics.SmoothingMode = SmoothingMode.AntiAlias; - graphics.FillPolygon(System.Drawing.Brushes.HotPink, - new[] - { - new Point(10, 10), - new Point(550, 50), - new Point(200, 400) - }); - } - using (MemoryStream ms = new MemoryStream()) + using (var stream = new MemoryStream()) { - destination.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp); + destination.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp); } } } @@ -53,7 +50,7 @@ namespace SixLabors.ImageSharp.Benchmarks [Benchmark(Description = "ImageSharp Fill Polygon")] public void DrawSolidPolygonCore() { - using (Image image = new Image(800, 800)) + using (var image = new Image(800, 800)) { image.Mutate(x => x.FillPolygon( Rgba32.HotPink, @@ -63,9 +60,9 @@ namespace SixLabors.ImageSharp.Benchmarks new Vector2(200, 400) })); - using (MemoryStream ms = new MemoryStream()) + using (var stream = new MemoryStream()) { - image.SaveAsBmp(ms); + image.SaveAsBmp(stream); } } } @@ -73,15 +70,15 @@ namespace SixLabors.ImageSharp.Benchmarks [Benchmark(Description = "ImageSharp Fill Polygon - cached shape")] public void DrawSolidPolygonCoreCahced() { - using (Image image = new Image(800, 800)) + using (var image = new Image(800, 800)) { image.Mutate(x => x.Fill( Rgba32.HotPink, this.shape)); - using (MemoryStream ms = new MemoryStream()) + using (var stream = new MemoryStream()) { - image.SaveAsBmp(ms); + image.SaveAsBmp(stream); } } } diff --git a/tests/ImageSharp.Benchmarks/Drawing/FillRectangle.cs b/tests/ImageSharp.Benchmarks/Drawing/FillRectangle.cs index 643e4ac9a1..d45b791aea 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/FillRectangle.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/FillRectangle.cs @@ -1,7 +1,5 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// using System.Drawing; using System.Drawing.Drawing2D; @@ -15,22 +13,18 @@ using CoreSize = SixLabors.Primitives.Size; namespace SixLabors.ImageSharp.Benchmarks { - - public class FillRectangle : BenchmarkBase { [Benchmark(Baseline = true, Description = "System.Drawing Fill Rectangle")] public Size FillRectangleSystemDrawing() { - using (Bitmap destination = new Bitmap(800, 800)) + using (var destination = new Bitmap(800, 800)) + using (var graphics = Graphics.FromImage(destination)) { - - using (Graphics graphics = Graphics.FromImage(destination)) - { - graphics.InterpolationMode = InterpolationMode.Default; - graphics.SmoothingMode = SmoothingMode.AntiAlias; - graphics.FillRectangle(System.Drawing.Brushes.HotPink, new Rectangle(10, 10, 190, 140)); - } + graphics.InterpolationMode = InterpolationMode.Default; + graphics.SmoothingMode = SmoothingMode.AntiAlias; + graphics.FillRectangle(System.Drawing.Brushes.HotPink, new Rectangle(10, 10, 190, 140)); + return destination.Size; } } @@ -38,7 +32,7 @@ namespace SixLabors.ImageSharp.Benchmarks [Benchmark(Description = "ImageSharp Fill Rectangle")] public CoreSize FillRactangleCore() { - using (Image image = new Image(800, 800)) + using (var image = new Image(800, 800)) { image.Mutate(x => x.Fill(Rgba32.HotPink, new CoreRectangle(10, 10, 190, 140))); @@ -49,7 +43,7 @@ namespace SixLabors.ImageSharp.Benchmarks [Benchmark(Description = "ImageSharp Fill Rectangle - As Polygon")] public CoreSize FillPolygonCore() { - using (Image image = new Image(800, 800)) + using (var image = new Image(800, 800)) { image.Mutate(x => x.FillPolygon( Rgba32.HotPink, diff --git a/tests/ImageSharp.Benchmarks/Drawing/FillWithPattern.cs b/tests/ImageSharp.Benchmarks/Drawing/FillWithPattern.cs index 3723619882..3c9cfd7e34 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/FillWithPattern.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/FillWithPattern.cs @@ -1,7 +1,5 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// using System.Drawing; using System.Drawing.Drawing2D; @@ -21,19 +19,19 @@ namespace SixLabors.ImageSharp.Benchmarks [Benchmark(Baseline = true, Description = "System.Drawing Fill with Pattern")] public void DrawPatternPolygonSystemDrawing() { - using (Bitmap destination = new Bitmap(800, 800)) + using (var destination = new Bitmap(800, 800)) + using (var graphics = Graphics.FromImage(destination)) { - using (Graphics graphics = Graphics.FromImage(destination)) + graphics.SmoothingMode = SmoothingMode.AntiAlias; + + using (var brush = new HatchBrush(HatchStyle.BackwardDiagonal, Color.HotPink)) { - graphics.SmoothingMode = SmoothingMode.AntiAlias; - using (var brush = new HatchBrush(HatchStyle.BackwardDiagonal, Color.HotPink)) - { - graphics.FillRectangle(brush, new Rectangle(0, 0, 800, 800)); // can't find a way to flood fill with a brush - } + graphics.FillRectangle(brush, new Rectangle(0, 0, 800, 800)); // can't find a way to flood fill with a brush } - using (MemoryStream ms = new MemoryStream()) + + using (var stream = new MemoryStream()) { - destination.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp); + destination.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp); } } } @@ -41,13 +39,13 @@ namespace SixLabors.ImageSharp.Benchmarks [Benchmark(Description = "ImageSharp Fill with Pattern")] public void DrawPatternPolygon3Core() { - using (Image image = new Image(800, 800)) + using (var image = new Image(800, 800)) { image.Mutate(x => x.Fill(CoreBrushes.BackwardDiagonal(Rgba32.HotPink))); - using (MemoryStream ms = new MemoryStream()) + using (var stream = new MemoryStream()) { - image.SaveAsBmp(ms); + image.SaveAsBmp(stream); } } } diff --git a/tests/ImageSharp.Benchmarks/General/Array2D.cs b/tests/ImageSharp.Benchmarks/General/Array2D.cs index 60d89847fd..1f8961fcde 100644 --- a/tests/ImageSharp.Benchmarks/General/Array2D.cs +++ b/tests/ImageSharp.Benchmarks/General/Array2D.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace SixLabors.ImageSharp.Benchmarks.General -{ - using System; +using System; - using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Attributes; - using SixLabors.ImageSharp.Primitives; +using SixLabors.ImageSharp.Primitives; +namespace SixLabors.ImageSharp.Benchmarks.General +{ /** * Method | Count | Mean | Error | StdDev | Scaled | ScaledSD | -------------------------------------------- |------ |---------:|---------:|---------:|-------:|---------:| diff --git a/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs b/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs index ac6b3f93c7..41c9ab6c7e 100644 --- a/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs +++ b/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs @@ -1,15 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace SixLabors.ImageSharp.Benchmarks.General -{ - using System; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using BenchmarkDotNet.Attributes; - +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using BenchmarkDotNet.Attributes; + +namespace SixLabors.ImageSharp.Benchmarks.General +{ [Config(typeof(Config.ShortClr))] public class ArrayCopy { diff --git a/tests/ImageSharp.Benchmarks/General/ArrayReverse.cs b/tests/ImageSharp.Benchmarks/General/ArrayReverse.cs index 45a8519a9c..c49c383eb8 100644 --- a/tests/ImageSharp.Benchmarks/General/ArrayReverse.cs +++ b/tests/ImageSharp.Benchmarks/General/ArrayReverse.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace SixLabors.ImageSharp.Benchmarks.General -{ - using System; +using System; - using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Attributes; +namespace SixLabors.ImageSharp.Benchmarks.General +{ public class ArrayReverse { [Params(4, 16, 32)] @@ -58,4 +56,4 @@ namespace SixLabors.ImageSharp.Benchmarks.General } } } -} +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/BasicMath/ClampInt32IntoByte.cs b/tests/ImageSharp.Benchmarks/General/BasicMath/ClampInt32IntoByte.cs index 6ce82ba115..a8686fc187 100644 --- a/tests/ImageSharp.Benchmarks/General/BasicMath/ClampInt32IntoByte.cs +++ b/tests/ImageSharp.Benchmarks/General/BasicMath/ClampInt32IntoByte.cs @@ -1,7 +1,5 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// using System; using System.Runtime.CompilerServices; diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToRgba32.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToRgba32.cs index d205e1e63e..ea8b34c249 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToRgba32.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToRgba32.cs @@ -1,7 +1,4 @@ -// ReSharper disable InconsistentNaming - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; using BenchmarkDotNet.Attributes; diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToVector4.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToVector4.cs index 2bc3ee9716..68a16b7919 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToVector4.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToVector4.cs @@ -3,8 +3,6 @@ using System.Runtime.CompilerServices; using BenchmarkDotNet.Attributes; -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion { public class PixelConversion_ConvertToVector4 diff --git a/tests/ImageSharp.Benchmarks/General/Vector4Constants.cs b/tests/ImageSharp.Benchmarks/General/Vector4Constants.cs index ae11806f32..3597207ee4 100644 --- a/tests/ImageSharp.Benchmarks/General/Vector4Constants.cs +++ b/tests/ImageSharp.Benchmarks/General/Vector4Constants.cs @@ -1,10 +1,10 @@ -namespace SixLabors.ImageSharp.Benchmarks.General -{ - using System; - using System.Numerics; +using System; +using System.Numerics; - using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Attributes; +namespace SixLabors.ImageSharp.Benchmarks.General +{ /// /// Has it any effect on performance to store SIMD constants as static readonly fields? Is it OK to always inline them? /// Spoiler: the difference seems to be statistically insignificant! diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/BitwiseOrUint32.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/BitwiseOrUint32.cs index d189411b5d..3afb796a7c 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/BitwiseOrUint32.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/BitwiseOrUint32.cs @@ -1,9 +1,9 @@ -namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization -{ - using System.Numerics; +using System.Numerics; - using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Attributes; +namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization +{ public class BitwiseOrUInt32 { private uint[] input; diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/DivFloat.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/DivFloat.cs index 6378467478..be9534f7d0 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/DivFloat.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/DivFloat.cs @@ -1,9 +1,9 @@ -namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization -{ - using System.Numerics; +using System.Numerics; - using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Attributes; +namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization +{ public class DivFloat { private float[] input; @@ -41,11 +41,11 @@ namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization [Benchmark] public void Simd() { - Vector v = new Vector(this.testValue); + var v = new Vector(this.testValue); for (int i = 0; i < this.input.Length; i += Vector.Count) { - Vector a = new Vector(this.input, i); + var a = new Vector(this.input, i); a = a / v; a.CopyTo(this.result, i); } diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/DivUInt32.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/DivUInt32.cs index 49ada2f368..bfc8d3de38 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/DivUInt32.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/DivUInt32.cs @@ -1,9 +1,9 @@ -namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization -{ - using System.Numerics; +using System.Numerics; - using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Attributes; +namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization +{ public class DivUInt32 { private uint[] input; @@ -32,6 +32,7 @@ namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization public void Standard() { uint v = this.testValue; + for (int i = 0; i < this.input.Length; i++) { this.result[i] = this.input[i] / v; @@ -41,11 +42,12 @@ namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization [Benchmark] public void Simd() { - Vector v = new Vector(this.testValue); + var v = new Vector(this.testValue); for (int i = 0; i < this.input.Length; i += Vector.Count) { - Vector a = new Vector(this.input, i); + var a = new Vector(this.input, i); + a = a / v; a.CopyTo(this.result, i); } diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/Divide.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/Divide.cs index b384295570..df09aa569a 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/Divide.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/Divide.cs @@ -1,10 +1,9 @@ -namespace ImageSharp.Benchmarks.General.Vectorization -{ - using System; - using System.Numerics; +using System.Numerics; - using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Attributes; +namespace ImageSharp.Benchmarks.General.Vectorization +{ public class DivFloat : SIMDBenchmarkBase.Divide { protected override float GetTestValue() => 42; diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/MulFloat.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/MulFloat.cs index 8c5f568181..418209cbc3 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/MulFloat.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/MulFloat.cs @@ -1,9 +1,9 @@ -namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization -{ - using System.Numerics; +using System.Numerics; - using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Attributes; +namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization +{ public class MulFloat { private float[] input; diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/MulUInt32.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/MulUInt32.cs index 913d4ce3c7..7253dbd6a1 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/MulUInt32.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/MulUInt32.cs @@ -1,9 +1,9 @@ -namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization -{ - using System.Numerics; +using System.Numerics; - using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Attributes; +namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization +{ public class MulUInt32 { private uint[] input; @@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization [Benchmark] public void Simd() { - Vector v = new Vector(this.testValue); + var v = new Vector(this.testValue); for (int i = 0; i < this.input.Length; i += Vector.Count) { diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/Multiply.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/Multiply.cs index d1b70f21b3..7a679c0009 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/Multiply.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/Multiply.cs @@ -1,8 +1,8 @@ +using System.Numerics; +using BenchmarkDotNet.Attributes; + namespace ImageSharp.Benchmarks.General.Vectorization { - using System.Numerics; - using BenchmarkDotNet.Attributes; - public class MulUInt32 : SIMDBenchmarkBase.Multiply { protected override uint GetTestValue() => 42u; diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/ReinterpretUInt32AsFloat.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/ReinterpretUInt32AsFloat.cs index f3853a8b1f..67a8a9f39e 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/ReinterpretUInt32AsFloat.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/ReinterpretUInt32AsFloat.cs @@ -1,10 +1,10 @@ -namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization -{ - using System.Numerics; - using System.Runtime.InteropServices; +using System.Numerics; +using System.Runtime.InteropServices; - using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Attributes; +namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization +{ public class ReinterpretUInt32AsFloat { private uint[] input; @@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization [Benchmark(Baseline = true)] public void Standard() { - UIntFloatUnion u = default(UIntFloatUnion); + UIntFloatUnion u = default; for (int i = 0; i < this.input.Length; i++) { u.i = this.input[i]; diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/SIMDBenchmarkBase.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/SIMDBenchmarkBase.cs index 76987dbd21..8fc9d9977a 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/SIMDBenchmarkBase.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/SIMDBenchmarkBase.cs @@ -1,10 +1,10 @@ -namespace ImageSharp.Benchmarks.General.Vectorization -{ - using System.Numerics; - using System.Runtime.CompilerServices; +using System.Numerics; +using System.Runtime.CompilerServices; - using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Attributes; +namespace ImageSharp.Benchmarks.General.Vectorization +{ public abstract class SIMDBenchmarkBase where T : struct { @@ -19,7 +19,6 @@ namespace ImageSharp.Benchmarks.General.Vectorization protected virtual T GetTestValue() => default(T); protected virtual Vector GetTestVector() => new Vector(this.GetTestValue()); - [Params(32)] public int InputSize { get; set; } diff --git a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj index f941203db1..e600af7851 100644 --- a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj +++ b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj @@ -16,10 +16,10 @@ - - + + - + diff --git a/tests/ImageSharp.Benchmarks/Program.cs b/tests/ImageSharp.Benchmarks/Program.cs index 4dd63067ac..5caf238fb2 100644 --- a/tests/ImageSharp.Benchmarks/Program.cs +++ b/tests/ImageSharp.Benchmarks/Program.cs @@ -1,15 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// + +using System.Reflection; + +using BenchmarkDotNet.Running; namespace SixLabors.ImageSharp.Benchmarks { - using BenchmarkDotNet.Running; - - using SixLabors.ImageSharp.Formats; - using System.Reflection; - public class Program { /// diff --git a/tests/ImageSharp.Benchmarks/Samplers/Crop.cs b/tests/ImageSharp.Benchmarks/Samplers/Crop.cs index 240a277cf0..4fe7a365f3 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/Crop.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/Crop.cs @@ -1,46 +1,40 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Benchmarks -{ - using System.Drawing; - using System.Drawing.Drawing2D; +using System.Drawing; +using System.Drawing.Drawing2D; - using BenchmarkDotNet.Attributes; - using SixLabors.ImageSharp.Processing; +using BenchmarkDotNet.Attributes; +using SixLabors.ImageSharp.Processing; - using CoreSize = SixLabors.Primitives.Size; +using CoreSize = SixLabors.Primitives.Size; +namespace SixLabors.ImageSharp.Benchmarks +{ public class Crop : BenchmarkBase { [Benchmark(Baseline = true, Description = "System.Drawing Crop")] public Size CropSystemDrawing() { - using (Bitmap source = new Bitmap(800, 800)) + using (var source = new Bitmap(800, 800)) + using (var destination = new Bitmap(100, 100)) + using (var graphics = Graphics.FromImage(destination)) { - using (Bitmap destination = new Bitmap(100, 100)) - { - using (Graphics graphics = Graphics.FromImage(destination)) - { - graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; - graphics.CompositingQuality = CompositingQuality.HighQuality; - graphics.DrawImage(source, new Rectangle(0, 0, 100, 100), 0, 0, 100, 100, GraphicsUnit.Pixel); - } - - return destination.Size; - } + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.DrawImage(source, new Rectangle(0, 0, 100, 100), 0, 0, 100, 100, GraphicsUnit.Pixel); + + return destination.Size; } } [Benchmark(Description = "ImageSharp Crop")] public CoreSize CropResizeCore() { - using (Image image = new Image(800, 800)) + using (var image = new Image(800, 800)) { image.Mutate(x => x.Crop(100, 100)); return new CoreSize(image.Width, image.Height); diff --git a/tests/ImageSharp.Benchmarks/Samplers/DetectEdges.cs b/tests/ImageSharp.Benchmarks/Samplers/DetectEdges.cs index 006d1b6391..b36b28ef33 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/DetectEdges.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/DetectEdges.cs @@ -1,7 +1,5 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// using SixLabors.ImageSharp.PixelFormats; @@ -53,4 +51,4 @@ namespace SixLabors.ImageSharp.Benchmarks this.image.Mutate(x => x.DetectEdges(EdgeDetectionOperators.Sobel)); } } -} +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Samplers/Glow.cs b/tests/ImageSharp.Benchmarks/Samplers/Glow.cs index 729971548c..15b82e022d 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/Glow.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/Glow.cs @@ -4,7 +4,6 @@ using System; using System.Buffers; using System.Numerics; -using System.Threading.Tasks; using BenchmarkDotNet.Attributes; @@ -35,7 +34,7 @@ namespace SixLabors.ImageSharp.Benchmarks [Benchmark(Description = "ImageSharp Glow - Bulk")] public CoreSize GlowBulk() { - using (Image image = new Image(800, 800)) + using (var image = new Image(800, 800)) { this.bulk.Apply(image, image.Bounds()); return new CoreSize(image.Width, image.Height); @@ -45,7 +44,7 @@ namespace SixLabors.ImageSharp.Benchmarks [Benchmark(Description = "ImageSharp Glow - Parallel")] public CoreSize GLowSimple() { - using (Image image = new Image(800, 800)) + using (var image = new Image(800, 800)) { this.parallel.Apply(image, image.Bounds()); return new CoreSize(image.Width, image.Height); @@ -128,7 +127,7 @@ namespace SixLabors.ImageSharp.Benchmarks int offsetX = x - startX; float distance = Vector2.Distance(centre, new Vector2(offsetX, offsetY)); Vector4 sourceColor = sourcePixels[offsetX, offsetY].ToVector4(); - TPixel packed = default(TPixel); + TPixel packed = default; packed.FromVector4( PremultipliedLerp( sourceColor, @@ -166,9 +165,9 @@ namespace SixLabors.ImageSharp.Benchmarks // https://en.wikipedia.org/wiki/Alpha_compositing // Vout = Vs + Vb (1 - Vsa) // Aout = Vsa + Vsb (1 - Vsa) - Vector3 inverseW = new Vector3(1 - source.W); - Vector3 xyzB = new Vector3(backdrop.X, backdrop.Y, backdrop.Z); - Vector3 xyzS = new Vector3(source.X, source.Y, source.Z); + var inverseW = new Vector3(1 - source.W); + var xyzB = new Vector3(backdrop.X, backdrop.Y, backdrop.Z); + var xyzS = new Vector3(source.X, source.Y, source.Z); return new Vector4(xyzS + (xyzB * inverseW), source.W + (backdrop.W * (1 - source.W))); } diff --git a/tests/ImageSharp.Benchmarks/Samplers/Resize.cs b/tests/ImageSharp.Benchmarks/Samplers/Resize.cs index 51f3a5653a..e99163f8b8 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/Resize.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/Resize.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; using System.Drawing; using System.Drawing.Drawing2D; diff --git a/tests/ImageSharp.Benchmarks/Samplers/Rotate.cs b/tests/ImageSharp.Benchmarks/Samplers/Rotate.cs index f898576af0..69ff1549bd 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/Rotate.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/Rotate.cs @@ -1,4 +1,7 @@ -using BenchmarkDotNet.Attributes; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.Primitives; diff --git a/tests/ImageSharp.Benchmarks/Samplers/Skew.cs b/tests/ImageSharp.Benchmarks/Samplers/Skew.cs index 84819750af..559e49704b 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/Skew.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/Skew.cs @@ -1,4 +1,7 @@ -using BenchmarkDotNet.Attributes; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.Primitives; From 1a13941836f88be0afc924cde81ddec59fe41de2 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 14 Apr 2019 17:51:30 +0200 Subject: [PATCH 057/223] ResizeWindowOld --- .../Transforms/Resize/ResizeProcessor.cs | 122 +++++++++++++++--- .../Transforms/ResizeKernelMapTests.cs | 7 +- .../Processors/Transforms/ResizeTests.cs | 13 +- 3 files changed, 119 insertions(+), 23 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs index 8c911cbfab..429b709ee3 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs @@ -244,26 +244,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms // A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm // First process the columns. Since we are not using multiple threads startY and endY // are the upper and lower bounds of the source rectangle. - using (Buffer2D firstPassPixelsTransposed = source.MemoryAllocator.Allocate2D(sourceHeight, width)) + //using (Buffer2D firstPassPixelsTransposed = source.MemoryAllocator.Allocate2D(sourceHeight, width)) + + using (var resizeWindow = new ResizeWindowOld( + configuration, + sourceRectangle, + conversionModifiers, + this.horizontalKernelMap, + this.verticalKernelMap, + sourceHeight, + width, + minX, + maxX, + startX)) using (IMemoryOwner tempBuffer = source.MemoryAllocator.Allocate(Math.Max(source.Width, width))) { - firstPassPixelsTransposed.MemorySource.Clear(); + Span tempRowSpan = tempBuffer.GetSpan().Slice(sourceX, source.Width - sourceX); - for (int y = 0; y < sourceRectangle.Bottom; y++) - { - Span sourceRow = source.GetPixelRowSpan(y).Slice(sourceX); - Span tempRowSpan = tempBuffer.GetSpan().Slice(sourceX, source.Width - sourceX); - - PixelOperations.Instance.ToVector4(configuration, sourceRow, tempRowSpan, conversionModifiers); - - ref Vector4 firstPassBaseRef = ref firstPassPixelsTransposed.Span[y]; - - for (int x = minX; x < maxX; x++) - { - ResizeKernel kernel = this.horizontalKernelMap.GetKernel(x - startX); - Unsafe.Add(ref firstPassBaseRef, x * sourceHeight) = kernel.Convolve(tempRowSpan); - } - } + resizeWindow.Initialize(source.PixelBuffer, tempRowSpan); // Now process the rows. Span tempColSpan = tempBuffer.GetSpan().Slice(0, width); @@ -277,7 +275,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms for (int x = 0; x < width; x++) { - Span firstPassColumn = firstPassPixelsTransposed.GetRowSpan(x).Slice(sourceY); + Span firstPassColumn = resizeWindow.GetColumnSpan(x); // Destination color components Unsafe.Add(ref tempRowBase, x) = kernel.Convolve(firstPassColumn); @@ -301,4 +299,92 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms this.verticalKernelMap = null; } } + + class ResizeWindowOld : IDisposable + { + private readonly Buffer2D buffer; + + private readonly Configuration configuration; + + private readonly Rectangle sourceRectangle; + + private readonly PixelConversionModifiers conversionModifiers; + + private readonly ResizeKernelMap horizontalKernelMap; + + private readonly ResizeKernelMap verticalKernelMap; + + private readonly int minX; + + private readonly int maxX; + + private readonly int startX; + + public ResizeWindowOld( + Configuration configuration, + Rectangle sourceRectangle, + PixelConversionModifiers conversionModifiers, + ResizeKernelMap horizontalKernelMap, + ResizeKernelMap verticalKernelMap, + int sourceHeight, + int destWidth, + int minX, + int maxX, + int startX) + { + this.configuration = configuration; + this.sourceRectangle = sourceRectangle; + this.conversionModifiers = conversionModifiers; + this.horizontalKernelMap = horizontalKernelMap; + this.verticalKernelMap = verticalKernelMap; + this.minX = minX; + this.maxX = maxX; + this.startX = startX; + this.buffer = configuration.MemoryAllocator.Allocate2D(sourceRectangle.Height, destWidth, AllocationOptions.Clean); + + this.Top = sourceRectangle.Top; + + this.Bottom = sourceRectangle.Bottom; + } + + public int Top { get; private set; } + + public int Bottom { get; private set; } + + public void Initialize( + Buffer2D source, + Span tempRowSpan) + where TPixel : struct, IPixel + { + for (int y = this.sourceRectangle.Top; y < this.sourceRectangle.Bottom; y++) + { + Span sourceRow = source.GetRowSpan(y).Slice(this.sourceRectangle.X); + + PixelOperations.Instance.ToVector4( + this.configuration, + sourceRow, + tempRowSpan, + this.conversionModifiers); + + ref Vector4 firstPassBaseRef = ref this.buffer.Span[y - this.sourceRectangle.Y]; + + for (int x = this.minX; x < this.maxX; x++) + { + ResizeKernel kernel = this.horizontalKernelMap.GetKernel(x - this.startX); + Unsafe.Add(ref firstPassBaseRef, x * this.sourceRectangle.Height) = kernel.Convolve(tempRowSpan); + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span GetColumnSpan(int x) + { + return this.buffer.GetRowSpan(x); + } + + public void Dispose() + { + this.buffer.Dispose(); + } + } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs index 5d3790f071..9de8665869 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs @@ -93,7 +93,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms GenerateImageResizeData(); - [Theory(Skip = "Only for debugging and development")] + [Theory( + Skip = "Only for debugging and development" + )] [MemberData(nameof(KernelMapData))] public void PrintNonNormalizedKernelMap(string resamplerName, int srcSize, int destSize) { @@ -130,7 +132,10 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms var referenceMap = ReferenceKernelMap.Calculate(resampler, destSize, srcSize); var kernelMap = ResizeKernelMap.Calculate(resampler, destSize, srcSize, Configuration.Default.MemoryAllocator); + + #if DEBUG + this.Output.WriteLine(kernelMap.Info); this.Output.WriteLine($"Expected KernelMap:\n{PrintKernelMap(referenceMap)}\n"); this.Output.WriteLine($"Actual KernelMap:\n{PrintKernelMap(kernelMap)}\n"); #endif diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs index 7f1f5a26a2..5cea8ddaea 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs @@ -5,6 +5,7 @@ using System; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Transforms; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; @@ -37,19 +38,23 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.RgbaVector; [Theory] - [WithBasicTestPatternImages(15, 12, PixelTypes.Rgba32)] - public void Resize_BasicSmall(TestImageProvider provider) + [WithBasicTestPatternImages(15, 12, PixelTypes.Rgba32, 2, 3, 1, 2)] + [WithBasicTestPatternImages(2, 256, PixelTypes.Rgba32, 1, 1, 1, 8)] + [WithBasicTestPatternImages(2, 32, PixelTypes.Rgba32, 1, 1, 1, 2)] + public void Resize_BasicSmall(TestImageProvider provider, int wN, int wD, int hN, int hD) where TPixel : struct, IPixel { // Basic test case, very helpful for debugging + // [WithBasicTestPatternImages(15, 12, PixelTypes.Rgba32, 2, 3, 1, 2)] means: // resizing: (15, 12) -> (10, 6) // kernel dimensions: (3, 4) using (Image image = provider.GetImage()) { - var destSize = new Size(image.Width * 2 / 3, image.Height / 2); + var destSize = new Size(image.Width * wN / wD, image.Height * hN / hD); image.Mutate(x => x.Resize(destSize, KnownResamplers.Bicubic, false)); - image.DebugSave(provider); + FormattableString outputInfo = $"({wN}÷{wD},{hN}÷{hD})"; + image.DebugSave(provider, outputInfo); } } From fd7c5db66bb0a6d740bd8b0117fa78d85dc78bad Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 14 Apr 2019 18:37:16 +0200 Subject: [PATCH 058/223] ResizeWindow refactor 1 --- .../Transforms/Resize/ResizeProcessor.cs | 115 ++---------------- .../Transforms/Resize/ResizeWindow.cs | 97 +++++++++++++++ 2 files changed, 106 insertions(+), 106 deletions(-) create mode 100644 src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWindow.cs diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs index 429b709ee3..707be1ec9d 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs @@ -195,19 +195,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms int sourceX = sourceRectangle.X; int sourceY = sourceRectangle.Y; int startY = this.TargetRectangle.Y; - int endY = this.TargetRectangle.Bottom; int startX = this.TargetRectangle.X; - int endX = this.TargetRectangle.Right; - int minX = Math.Max(0, startX); - int maxX = Math.Min(width, endX); - int minY = Math.Max(0, startY); - int maxY = Math.Min(height, endY); + var workingRect = Rectangle.Intersect( + this.TargetRectangle, + new Rectangle(0, 0, width, height)); if (this.Sampler is NearestNeighborResampler) { - var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY); - // Scaling factors float widthFactor = sourceRectangle.Width / (float)this.TargetRectangle.Width; float heightFactor = sourceRectangle.Height / (float)this.TargetRectangle.Height; @@ -224,7 +219,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms source.GetPixelRowSpan((int)(((y - startY) * heightFactor) + sourceY)); Span targetRow = destination.GetPixelRowSpan(y); - for (int x = minX; x < maxX; x++) + for (int x = workingRect.Left; x < workingRect.Right; x++) { // X coordinates of source points targetRow[x] = sourceRow[(int)(((x - startX) * widthFactor) + sourceX)]; @@ -244,29 +239,25 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms // A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm // First process the columns. Since we are not using multiple threads startY and endY // are the upper and lower bounds of the source rectangle. - //using (Buffer2D firstPassPixelsTransposed = source.MemoryAllocator.Allocate2D(sourceHeight, width)) - - using (var resizeWindow = new ResizeWindowOld( + using (var resizeWindow = new ResizeWindow( configuration, sourceRectangle, conversionModifiers, this.horizontalKernelMap, this.verticalKernelMap, - sourceHeight, width, - minX, - maxX, + workingRect, startX)) - using (IMemoryOwner tempBuffer = source.MemoryAllocator.Allocate(Math.Max(source.Width, width))) + using (IMemoryOwner tempBuffer = source.MemoryAllocator.Allocate(Math.Max(sourceRectangle.Width, width))) { - Span tempRowSpan = tempBuffer.GetSpan().Slice(sourceX, source.Width - sourceX); + Span tempRowSpan = tempBuffer.GetSpan(); resizeWindow.Initialize(source.PixelBuffer, tempRowSpan); // Now process the rows. Span tempColSpan = tempBuffer.GetSpan().Slice(0, width); - for (int y = minY; y < maxY; y++) + for (int y = workingRect.Top; y < workingRect.Bottom; y++) { // Ensure offsets are normalized for cropping and padding. ResizeKernel kernel = this.verticalKernelMap.GetKernel(y - startY); @@ -299,92 +290,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms this.verticalKernelMap = null; } } - - class ResizeWindowOld : IDisposable - { - private readonly Buffer2D buffer; - - private readonly Configuration configuration; - - private readonly Rectangle sourceRectangle; - - private readonly PixelConversionModifiers conversionModifiers; - - private readonly ResizeKernelMap horizontalKernelMap; - - private readonly ResizeKernelMap verticalKernelMap; - - private readonly int minX; - - private readonly int maxX; - - private readonly int startX; - - public ResizeWindowOld( - Configuration configuration, - Rectangle sourceRectangle, - PixelConversionModifiers conversionModifiers, - ResizeKernelMap horizontalKernelMap, - ResizeKernelMap verticalKernelMap, - int sourceHeight, - int destWidth, - int minX, - int maxX, - int startX) - { - this.configuration = configuration; - this.sourceRectangle = sourceRectangle; - this.conversionModifiers = conversionModifiers; - this.horizontalKernelMap = horizontalKernelMap; - this.verticalKernelMap = verticalKernelMap; - this.minX = minX; - this.maxX = maxX; - this.startX = startX; - this.buffer = configuration.MemoryAllocator.Allocate2D(sourceRectangle.Height, destWidth, AllocationOptions.Clean); - - this.Top = sourceRectangle.Top; - - this.Bottom = sourceRectangle.Bottom; - } - - public int Top { get; private set; } - - public int Bottom { get; private set; } - - public void Initialize( - Buffer2D source, - Span tempRowSpan) - where TPixel : struct, IPixel - { - for (int y = this.sourceRectangle.Top; y < this.sourceRectangle.Bottom; y++) - { - Span sourceRow = source.GetRowSpan(y).Slice(this.sourceRectangle.X); - - PixelOperations.Instance.ToVector4( - this.configuration, - sourceRow, - tempRowSpan, - this.conversionModifiers); - - ref Vector4 firstPassBaseRef = ref this.buffer.Span[y - this.sourceRectangle.Y]; - - for (int x = this.minX; x < this.maxX; x++) - { - ResizeKernel kernel = this.horizontalKernelMap.GetKernel(x - this.startX); - Unsafe.Add(ref firstPassBaseRef, x * this.sourceRectangle.Height) = kernel.Convolve(tempRowSpan); - } - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Span GetColumnSpan(int x) - { - return this.buffer.GetRowSpan(x); - } - - public void Dispose() - { - this.buffer.Dispose(); - } - } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWindow.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWindow.cs new file mode 100644 index 0000000000..130dd93a4b --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWindow.cs @@ -0,0 +1,97 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Memory; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Transforms +{ + class ResizeWindow : IDisposable + { + private readonly Buffer2D buffer; + + private readonly Configuration configuration; + + private readonly Rectangle sourceRectangle; + + private readonly PixelConversionModifiers conversionModifiers; + + private readonly ResizeKernelMap horizontalKernelMap; + + private readonly ResizeKernelMap verticalKernelMap; + + private readonly Rectangle workingRectangle; + + private readonly int startX; + + public ResizeWindow( + Configuration configuration, + Rectangle sourceRectangle, + PixelConversionModifiers conversionModifiers, + ResizeKernelMap horizontalKernelMap, + ResizeKernelMap verticalKernelMap, + int destWidth, + Rectangle workingRectangle, + int startX) + { + this.configuration = configuration; + this.sourceRectangle = sourceRectangle; + this.conversionModifiers = conversionModifiers; + this.horizontalKernelMap = horizontalKernelMap; + this.verticalKernelMap = verticalKernelMap; + this.workingRectangle = workingRectangle; + this.startX = startX; + this.buffer = configuration.MemoryAllocator.Allocate2D(sourceRectangle.Height, destWidth, AllocationOptions.Clean); + + this.Top = sourceRectangle.Top; + + this.Bottom = sourceRectangle.Bottom; + } + + public int Top { get; private set; } + + public int Bottom { get; private set; } + + public void Initialize( + Buffer2D source, + Span tempRowSpan) + where TPixel : struct, IPixel + { + for (int y = this.sourceRectangle.Top; y < this.sourceRectangle.Bottom; y++) + { + Span sourceRow = source.GetRowSpan(y).Slice(this.sourceRectangle.X); + + PixelOperations.Instance.ToVector4( + this.configuration, + sourceRow, + tempRowSpan, + this.conversionModifiers); + + ref Vector4 firstPassBaseRef = ref this.buffer.Span[y - this.sourceRectangle.Y]; + + for (int x = this.workingRectangle.Left; x < this.workingRectangle.Right; x++) + { + ResizeKernel kernel = this.horizontalKernelMap.GetKernel(x - this.startX); + Unsafe.Add(ref firstPassBaseRef, x * this.sourceRectangle.Height) = kernel.Convolve(tempRowSpan); + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span GetColumnSpan(int x) + { + return this.buffer.GetRowSpan(x); + } + + public void Dispose() + { + this.buffer.Dispose(); + } + } +} \ No newline at end of file From c337c4cb0fef917ef7a25d53135d377ec84542e2 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 14 Apr 2019 21:19:43 +0200 Subject: [PATCH 059/223] ResizeWindow refactor 2 --- .../Transforms/Resize/ResizeProcessor.cs | 14 ++-- .../Transforms/Resize/ResizeWindow.cs | 67 +++++++++++-------- 2 files changed, 45 insertions(+), 36 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs index 707be1ec9d..b4b537e200 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs @@ -235,27 +235,27 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms PixelConversionModifiers conversionModifiers = PixelConversionModifiers.Premultiply.ApplyCompanding(this.Compand); + BufferArea sourceArea = source.PixelBuffer.GetArea(sourceRectangle); + // Interpolate the image using the calculated weights. // A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm // First process the columns. Since we are not using multiple threads startY and endY // are the upper and lower bounds of the source rectangle. - using (var resizeWindow = new ResizeWindow( + using (var resizeWindow = new ResizeWindow( configuration, - sourceRectangle, + sourceArea, conversionModifiers, this.horizontalKernelMap, this.verticalKernelMap, width, workingRect, startX)) - using (IMemoryOwner tempBuffer = source.MemoryAllocator.Allocate(Math.Max(sourceRectangle.Width, width))) + using (IMemoryOwner tempBuffer = source.MemoryAllocator.Allocate(width)) { - Span tempRowSpan = tempBuffer.GetSpan(); - - resizeWindow.Initialize(source.PixelBuffer, tempRowSpan); + resizeWindow.Initialize(); // Now process the rows. - Span tempColSpan = tempBuffer.GetSpan().Slice(0, width); + Span tempColSpan = tempBuffer.GetSpan(); for (int y = workingRect.Top; y < workingRect.Bottom; y++) { diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWindow.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWindow.cs index 130dd93a4b..bd68fdba78 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWindow.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWindow.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.Numerics; using System.Runtime.CompilerServices; @@ -12,27 +13,32 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { - class ResizeWindow : IDisposable + class ResizeWindow : IDisposable + where TPixel : struct, IPixel { private readonly Buffer2D buffer; private readonly Configuration configuration; - private readonly Rectangle sourceRectangle; - private readonly PixelConversionModifiers conversionModifiers; private readonly ResizeKernelMap horizontalKernelMap; - private readonly ResizeKernelMap verticalKernelMap; + private readonly BufferArea source; - private readonly Rectangle workingRectangle; + private readonly Rectangle sourceRectangle; private readonly int startX; + private readonly IMemoryOwner tempRowBuffer; + + private readonly ResizeKernelMap verticalKernelMap; + + private readonly Rectangle workingRectangle; + public ResizeWindow( Configuration configuration, - Rectangle sourceRectangle, + BufferArea source, PixelConversionModifiers conversionModifiers, ResizeKernelMap horizontalKernelMap, ResizeKernelMap verticalKernelMap, @@ -41,31 +47,45 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms int startX) { this.configuration = configuration; - this.sourceRectangle = sourceRectangle; + this.source = source; + this.sourceRectangle = source.Rectangle; this.conversionModifiers = conversionModifiers; this.horizontalKernelMap = horizontalKernelMap; this.verticalKernelMap = verticalKernelMap; this.workingRectangle = workingRectangle; this.startX = startX; - this.buffer = configuration.MemoryAllocator.Allocate2D(sourceRectangle.Height, destWidth, AllocationOptions.Clean); + this.buffer = configuration.MemoryAllocator.Allocate2D( + this.sourceRectangle.Height, + destWidth, + AllocationOptions.Clean); + this.tempRowBuffer = configuration.MemoryAllocator.Allocate(this.sourceRectangle.Width); - this.Top = sourceRectangle.Top; + this.Top = this.sourceRectangle.Top; - this.Bottom = sourceRectangle.Bottom; + this.Bottom = this.sourceRectangle.Bottom; } + public int Bottom { get; private set; } + public int Top { get; private set; } - public int Bottom { get; private set; } + public void Dispose() + { + this.buffer.Dispose(); + } - public void Initialize( - Buffer2D source, - Span tempRowSpan) - where TPixel : struct, IPixel + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span GetColumnSpan(int x) { - for (int y = this.sourceRectangle.Top; y < this.sourceRectangle.Bottom; y++) + return this.buffer.GetRowSpan(x); + } + + public void Initialize() + { + Span tempRowSpan = this.tempRowBuffer.GetSpan(); + for (int y = 0; y < this.sourceRectangle.Height; y++) { - Span sourceRow = source.GetRowSpan(y).Slice(this.sourceRectangle.X); + Span sourceRow = this.source.GetRowSpan(y); PixelOperations.Instance.ToVector4( this.configuration, @@ -73,7 +93,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms tempRowSpan, this.conversionModifiers); - ref Vector4 firstPassBaseRef = ref this.buffer.Span[y - this.sourceRectangle.Y]; + ref Vector4 firstPassBaseRef = ref this.buffer.Span[y]; for (int x = this.workingRectangle.Left; x < this.workingRectangle.Right; x++) { @@ -82,16 +102,5 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } } } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Span GetColumnSpan(int x) - { - return this.buffer.GetRowSpan(x); - } - - public void Dispose() - { - this.buffer.Dispose(); - } } } \ No newline at end of file From 1362f0d0f5d3b51b663b8d15094687e067d2b7da Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 14 Apr 2019 21:59:06 +0200 Subject: [PATCH 060/223] ResizeWindow refactor 3 --- .../Transforms/Resize/ResizeKernelMap.cs | 11 +- .../Transforms/Resize/ResizeProcessor.cs | 5 + .../Transforms/Resize/ResizeWindow.cs | 12 +- .../Processors/Transforms/ResizeTests.cs | 433 +++++++++--------- 4 files changed, 245 insertions(+), 216 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs index 2ab574df2a..9abbb66e3a 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs @@ -54,11 +54,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms this.radius = radius; this.sourceLength = sourceLength; this.DestinationLength = destinationLength; - int maxWidth = (radius * 2) + 1; - this.data = memoryAllocator.Allocate2D(maxWidth, bufferHeight, AllocationOptions.Clean); + this.MaxDiameter = (radius * 2) + 1; + this.data = memoryAllocator.Allocate2D(this.MaxDiameter, bufferHeight, AllocationOptions.Clean); this.pinHandle = this.data.Memory.Pin(); this.kernels = new ResizeKernel[destinationLength]; - this.tempValues = new double[maxWidth]; + this.tempValues = new double[this.MaxDiameter]; } /// @@ -66,6 +66,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// public int DestinationLength { get; } + /// + /// Gets the maximum diameter of the kernels. + /// + public int MaxDiameter { get; } + /// /// Gets a string of information to help debugging /// diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs index b4b537e200..38419b5dd6 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs @@ -262,6 +262,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms // Ensure offsets are normalized for cropping and padding. ResizeKernel kernel = this.verticalKernelMap.GetKernel(y - startY); + if (kernel.Left + kernel.Length > resizeWindow.Bottom) + { + resizeWindow.Slide(); + } + ref Vector4 tempRowBase = ref MemoryMarshal.GetReference(tempColSpan); for (int x = 0; x < width; x++) diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWindow.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWindow.cs index bd68fdba78..05015dee20 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWindow.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWindow.cs @@ -13,7 +13,7 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { - class ResizeWindow : IDisposable + internal class ResizeWindow : IDisposable where TPixel : struct, IPixel { private readonly Buffer2D buffer; @@ -36,6 +36,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms private readonly Rectangle workingRectangle; + private readonly int diameter; + public ResizeWindow( Configuration configuration, BufferArea source, @@ -54,6 +56,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms this.verticalKernelMap = verticalKernelMap; this.workingRectangle = workingRectangle; this.startX = startX; + + this.diameter = verticalKernelMap.MaxDiameter; + this.buffer = configuration.MemoryAllocator.Allocate2D( this.sourceRectangle.Height, destWidth, @@ -102,5 +107,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } } } + + public void Slide() + { + throw new InvalidOperationException("Shouldn't happen yet!"); + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs index 5cea8ddaea..8c59fff26b 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs @@ -5,38 +5,92 @@ using System; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Transforms; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using SixLabors.Primitives; using Xunit; + // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms { public class ResizeTests { - public static readonly string[] CommonTestImages = { TestImages.Png.CalliphoraPartial }; + private const PixelTypes CommonNonDefaultPixelTypes = + PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.RgbaVector; - private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.07F); + private const PixelTypes DefaultPixelType = PixelTypes.Rgba32; public static readonly string[] AllResamplerNames = TestUtils.GetAllResamplerNames(); + public static readonly string[] CommonTestImages = { TestImages.Png.CalliphoraPartial }; + public static readonly string[] SmokeTestResamplerNames = { - nameof(KnownResamplers.NearestNeighbor), - nameof(KnownResamplers.Bicubic), - nameof(KnownResamplers.Box), + nameof(KnownResamplers.NearestNeighbor), nameof(KnownResamplers.Bicubic), nameof(KnownResamplers.Box), nameof(KnownResamplers.Lanczos5), }; - private const PixelTypes DefaultPixelType = PixelTypes.Rgba32; + private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.07F); + + [Theory] + [InlineData(-2, 0)] + [InlineData(-1, 0)] + [InlineData(0, 1)] + [InlineData(1, 0)] + [InlineData(2, 0)] + public static void BicubicWindowOscillatesCorrectly(float x, float expected) + { + IResampler sampler = KnownResamplers.Bicubic; + float result = sampler.GetValue(x); + + Assert.Equal(result, expected); + } + + [Theory] + [InlineData(-2, 0)] + [InlineData(-1, 0)] + [InlineData(0, 1)] + [InlineData(1, 0)] + [InlineData(2, 0)] + public static void Lanczos3WindowOscillatesCorrectly(float x, float expected) + { + IResampler sampler = KnownResamplers.Lanczos3; + float result = sampler.GetValue(x); + + Assert.Equal(result, expected); + } + + [Theory] + [InlineData(-4, 0)] + [InlineData(-2, 0)] + [InlineData(0, 1)] + [InlineData(2, 0)] + [InlineData(4, 0)] + public static void Lanczos5WindowOscillatesCorrectly(float x, float expected) + { + IResampler sampler = KnownResamplers.Lanczos5; + float result = sampler.GetValue(x); + + Assert.Equal(result, expected); + } + + [Theory] + [InlineData(-2, 0)] + [InlineData(-1, 0)] + [InlineData(0, 1)] + [InlineData(1, 0)] + [InlineData(2, 0)] + public static void TriangleWindowOscillatesCorrectly(float x, float expected) + { + IResampler sampler = KnownResamplers.Triangle; + float result = sampler.GetValue(x); + + Assert.Equal(result, expected); + } - private const PixelTypes CommonNonDefaultPixelTypes = - PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.RgbaVector; - [Theory] [WithBasicTestPatternImages(15, 12, PixelTypes.Rgba32, 2, 3, 1, 2)] [WithBasicTestPatternImages(2, 256, PixelTypes.Rgba32, 1, 1, 1, 8)] @@ -54,14 +108,117 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms var destSize = new Size(image.Width * wN / wD, image.Height * hN / hD); image.Mutate(x => x.Resize(destSize, KnownResamplers.Bicubic, false)); FormattableString outputInfo = $"({wN}÷{wD},{hN}÷{hD})"; - image.DebugSave(provider, outputInfo); + image.DebugSave(provider, outputInfo, appendPixelTypeToFileName: false); + image.CompareToReferenceOutput(provider, outputInfo, appendPixelTypeToFileName: false); + } + } + + [Theory] + [WithTestPatternImages(100, 100, DefaultPixelType)] + public void Resize_Compand(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Resize(image.Size() / 2, true)); + + image.DebugSave(provider); + image.CompareToReferenceOutput(ValidatorComparer, provider); + } + } + + [Theory] + [WithFile(TestImages.Png.Kaboom, DefaultPixelType, false)] + [WithFile(TestImages.Png.Kaboom, DefaultPixelType, true)] + public void Resize_DoesNotBleedAlphaPixels(TestImageProvider provider, bool compand) + where TPixel : struct, IPixel + { + string details = compand ? "Compand" : ""; + + provider.RunValidatingProcessorTest( + x => x.Resize(x.GetCurrentSize() / 2, compand), + details, + appendPixelTypeToFileName: false, + appendSourceFileOrDescription: false); + } + + [Theory] + [WithFile(TestImages.Gif.Giphy, DefaultPixelType)] + public void Resize_IsAppliedToAllFrames(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Resize(image.Width / 2, image.Height / 2, KnownResamplers.Bicubic)); + + // Comparer fights decoder with gif-s. Could not use CompareToReferenceOutput here :( + image.DebugSave(provider, extension: "gif"); + } + } + + [Theory] + [WithTestPatternImages(50, 50, CommonNonDefaultPixelTypes)] + public void Resize_IsNotBoundToSinglePixelType(TestImageProvider provider) + where TPixel : struct, IPixel + { + provider.RunValidatingProcessorTest(x => x.Resize(x.GetCurrentSize() / 2), comparer: ValidatorComparer); + } + + [Theory] + [WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] + public void Resize_ThrowsForWrappedMemoryImage(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image0 = provider.GetImage()) + { + var mmg = TestMemoryManager.CreateAsCopyOf(image0.GetPixelSpan()); + + using (var image1 = Image.WrapMemory(mmg.Memory, image0.Width, image0.Height)) + { + Assert.ThrowsAny( + () => { image1.Mutate(x => x.Resize(image0.Width / 2, image0.Height / 2, true)); }); + } } } + [Theory] + [WithFileCollection(nameof(CommonTestImages), DefaultPixelType, 1)] + [WithFileCollection(nameof(CommonTestImages), DefaultPixelType, 4)] + [WithFileCollection(nameof(CommonTestImages), DefaultPixelType, 8)] + [WithFileCollection(nameof(CommonTestImages), DefaultPixelType, -1)] + public void Resize_WorksWithAllParallelismLevels( + TestImageProvider provider, + int maxDegreeOfParallelism) + where TPixel : struct, IPixel + { + provider.Configuration.MaxDegreeOfParallelism = + maxDegreeOfParallelism > 0 ? maxDegreeOfParallelism : Environment.ProcessorCount; + + FormattableString details = $"MDP{maxDegreeOfParallelism}"; + + provider.RunValidatingProcessorTest( + x => x.Resize(x.GetCurrentSize() / 2), + details, + appendPixelTypeToFileName: false, + appendSourceFileOrDescription: false); + } + [Theory] [WithFileCollection(nameof(CommonTestImages), nameof(AllResamplerNames), DefaultPixelType, 0.5f, null, null)] - [WithFileCollection(nameof(CommonTestImages), nameof(SmokeTestResamplerNames), DefaultPixelType, 0.3f, null, null)] - [WithFileCollection(nameof(CommonTestImages), nameof(SmokeTestResamplerNames), DefaultPixelType, 1.8f, null, null)] + [WithFileCollection( + nameof(CommonTestImages), + nameof(SmokeTestResamplerNames), + DefaultPixelType, + 0.3f, + null, + null)] + [WithFileCollection( + nameof(CommonTestImages), + nameof(SmokeTestResamplerNames), + DefaultPixelType, + 1.8f, + null, + null)] [WithTestPatternImages(nameof(SmokeTestResamplerNames), 100, 100, DefaultPixelType, 0.5f, null, null)] [WithTestPatternImages(nameof(SmokeTestResamplerNames), 100, 100, DefaultPixelType, 1f, null, null)] [WithTestPatternImages(nameof(SmokeTestResamplerNames), 50, 50, DefaultPixelType, 8f, null, null)] @@ -84,15 +241,14 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms // Resize_WorksWithAllResamplers_TestPattern301x1180_NearestNeighbor-300x480.png // TODO: Should we investigate this? bool allowHigherInaccuracy = !TestEnvironment.Is64BitProcess - && string.IsNullOrEmpty(TestEnvironment.NetCoreVersion) - && sampler is NearestNeighborResampler; + && string.IsNullOrEmpty(TestEnvironment.NetCoreVersion) + && sampler is NearestNeighborResampler; var comparer = ImageComparer.TolerantPercentage(allowHigherInaccuracy ? 0.3f : 0.017f); provider.RunValidatingProcessorTest( ctx => { - SizeF newSize; string destSizeInfo; if (ratio.HasValue) @@ -126,107 +282,41 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms } [Theory] - [WithFileCollection(nameof(CommonTestImages), DefaultPixelType, 1)] - [WithFileCollection(nameof(CommonTestImages), DefaultPixelType, 4)] - [WithFileCollection(nameof(CommonTestImages), DefaultPixelType, 8)] - [WithFileCollection(nameof(CommonTestImages), DefaultPixelType, -1)] - public void Resize_WorksWithAllParallelismLevels(TestImageProvider provider, int maxDegreeOfParallelism) - where TPixel : struct, IPixel - { - provider.Configuration.MaxDegreeOfParallelism = - maxDegreeOfParallelism > 0 ? maxDegreeOfParallelism : Environment.ProcessorCount; - - FormattableString details = $"MDP{maxDegreeOfParallelism}"; - - provider.RunValidatingProcessorTest( - x => x.Resize(x.GetCurrentSize() / 2), - details, - appendPixelTypeToFileName: false, - appendSourceFileOrDescription: false); - } - - [Theory] - [WithTestPatternImages(100, 100, DefaultPixelType)] - public void Resize_Compand(TestImageProvider provider) + [WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] + public void ResizeFromSourceRectangle(TestImageProvider provider) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) { - image.Mutate(x => x.Resize(image.Size() / 2, true)); + var sourceRectangle = new Rectangle( + image.Width / 8, + image.Height / 8, + image.Width / 4, + image.Height / 4); + var destRectangle = new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2); + + image.Mutate( + x => x.Resize( + image.Width, + image.Height, + KnownResamplers.Bicubic, + sourceRectangle, + destRectangle, + false)); image.DebugSave(provider); image.CompareToReferenceOutput(ValidatorComparer, provider); } } - [Theory] - [WithTestPatternImages(50, 50, CommonNonDefaultPixelTypes)] - public void Resize_IsNotBoundToSinglePixelType(TestImageProvider provider) - where TPixel : struct, IPixel - { - provider.RunValidatingProcessorTest(x => x.Resize(x.GetCurrentSize() / 2), comparer: ValidatorComparer); - } - - [Theory] - [WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] - public void Resize_ThrowsForWrappedMemoryImage(TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image image0 = provider.GetImage()) - { - var mmg = TestMemoryManager.CreateAsCopyOf(image0.GetPixelSpan()); - - using (var image1 = Image.WrapMemory(mmg.Memory, image0.Width, image0.Height)) - { - Assert.ThrowsAny( - () => - { - image1.Mutate(x => x.Resize(image0.Width / 2, image0.Height / 2, true)); - }); - } - } - } - - [Theory] - [WithFile(TestImages.Png.Kaboom, DefaultPixelType, false)] - [WithFile(TestImages.Png.Kaboom, DefaultPixelType, true)] - public void Resize_DoesNotBleedAlphaPixels(TestImageProvider provider, bool compand) - where TPixel : struct, IPixel - { - string details = compand ? "Compand" : ""; - - provider.RunValidatingProcessorTest( - x => x.Resize(x.GetCurrentSize() / 2, compand), - details, - appendPixelTypeToFileName: false, - appendSourceFileOrDescription: false); - } - - [Theory] - [WithFile(TestImages.Gif.Giphy, DefaultPixelType)] - public void Resize_IsAppliedToAllFrames(TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Mutate(x => x.Resize(image.Width / 2, image.Height / 2, KnownResamplers.Bicubic)); - - // Comparer fights decoder with gif-s. Could not use CompareToReferenceOutput here :( - image.DebugSave(provider, extension: "gif"); - } - } - [Theory] [WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] - public void ResizeFromSourceRectangle(TestImageProvider provider) + public void ResizeHeightAndKeepAspect(TestImageProvider provider) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) { - var sourceRectangle = new Rectangle(image.Width / 8, image.Height / 8, image.Width / 4, image.Height / 4); - var destRectangle = new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2); - - image.Mutate(x => x.Resize(image.Width, image.Height, KnownResamplers.Bicubic, sourceRectangle, destRectangle, false)); + image.Mutate(x => x.Resize(0, image.Height / 3, false)); image.DebugSave(provider); image.CompareToReferenceOutput(ValidatorComparer, provider); @@ -234,27 +324,26 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms } [Theory] - [WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] - public void ResizeWidthAndKeepAspect(TestImageProvider provider) + [WithTestPatternImages(10, 100, DefaultPixelType)] + public void ResizeHeightCannotKeepAspectKeepsOnePixel(TestImageProvider provider) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) { - image.Mutate(x => x.Resize(image.Width / 3, 0, false)); - - image.DebugSave(provider); - image.CompareToReferenceOutput(ValidatorComparer, provider); + image.Mutate(x => x.Resize(0, 5)); + Assert.Equal(1, image.Width); + Assert.Equal(5, image.Height); } } [Theory] [WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] - public void ResizeHeightAndKeepAspect(TestImageProvider provider) + public void ResizeWidthAndKeepAspect(TestImageProvider provider) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) { - image.Mutate(x => x.Resize(0, image.Height / 3, false)); + image.Mutate(x => x.Resize(image.Width / 3, 0, false)); image.DebugSave(provider); image.CompareToReferenceOutput(ValidatorComparer, provider); @@ -274,30 +363,17 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms } } - [Theory] - [WithTestPatternImages(10, 100, DefaultPixelType)] - public void ResizeHeightCannotKeepAspectKeepsOnePixel(TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Mutate(x => x.Resize(0, 5)); - Assert.Equal(1, image.Width); - Assert.Equal(5, image.Height); - } - } - [Theory] [WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] - public void ResizeWithCropWidthMode(TestImageProvider provider) + public void ResizeWithBoxPadMode(TestImageProvider provider) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) { var options = new ResizeOptions - { - Size = new Size(image.Width / 2, image.Height) - }; + { + Size = new Size(image.Width + 200, image.Height + 200), Mode = ResizeMode.BoxPad + }; image.Mutate(x => x.Resize(options)); @@ -313,10 +389,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms { using (Image image = provider.GetImage()) { - var options = new ResizeOptions - { - Size = new Size(image.Width, image.Height / 2) - }; + var options = new ResizeOptions { Size = new Size(image.Width, image.Height / 2) }; image.Mutate(x => x.Resize(options)); @@ -327,16 +400,12 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms [Theory] [WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] - public void ResizeWithPadMode(TestImageProvider provider) + public void ResizeWithCropWidthMode(TestImageProvider provider) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) { - var options = new ResizeOptions - { - Size = new Size(image.Width + 200, image.Height), - Mode = ResizeMode.Pad - }; + var options = new ResizeOptions { Size = new Size(image.Width / 2, image.Height) }; image.Mutate(x => x.Resize(options)); @@ -347,16 +416,12 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms [Theory] [WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] - public void ResizeWithBoxPadMode(TestImageProvider provider) + public void ResizeWithMaxMode(TestImageProvider provider) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) { - var options = new ResizeOptions - { - Size = new Size(image.Width + 200, image.Height + 200), - Mode = ResizeMode.BoxPad - }; + var options = new ResizeOptions { Size = new Size(300, 300), Mode = ResizeMode.Max }; image.Mutate(x => x.Resize(options)); @@ -367,16 +432,18 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms [Theory] [WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] - public void ResizeWithMaxMode(TestImageProvider provider) + public void ResizeWithMinMode(TestImageProvider provider) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) { var options = new ResizeOptions - { - Size = new Size(300, 300), - Mode = ResizeMode.Max - }; + { + Size = new Size( + (int)Math.Round(image.Width * .75F), + (int)Math.Round(image.Height * .95F)), + Mode = ResizeMode.Min + }; image.Mutate(x => x.Resize(options)); @@ -387,16 +454,15 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms [Theory] [WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] - public void ResizeWithMinMode(TestImageProvider provider) + public void ResizeWithPadMode(TestImageProvider provider) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) { var options = new ResizeOptions - { - Size = new Size((int)Math.Round(image.Width * .75F), (int)Math.Round(image.Height * .95F)), - Mode = ResizeMode.Min - }; + { + Size = new Size(image.Width + 200, image.Height), Mode = ResizeMode.Pad + }; image.Mutate(x => x.Resize(options)); @@ -413,10 +479,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms using (Image image = provider.GetImage()) { var options = new ResizeOptions - { - Size = new Size(image.Width / 2, image.Height), - Mode = ResizeMode.Stretch - }; + { + Size = new Size(image.Width / 2, image.Height), Mode = ResizeMode.Stretch + }; image.Mutate(x => x.Resize(options)); @@ -424,61 +489,5 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms image.CompareToReferenceOutput(ValidatorComparer, provider); } } - - [Theory] - [InlineData(-2, 0)] - [InlineData(-1, 0)] - [InlineData(0, 1)] - [InlineData(1, 0)] - [InlineData(2, 0)] - public static void BicubicWindowOscillatesCorrectly(float x, float expected) - { - IResampler sampler = KnownResamplers.Bicubic; - float result = sampler.GetValue(x); - - Assert.Equal(result, expected); - } - - [Theory] - [InlineData(-2, 0)] - [InlineData(-1, 0)] - [InlineData(0, 1)] - [InlineData(1, 0)] - [InlineData(2, 0)] - public static void TriangleWindowOscillatesCorrectly(float x, float expected) - { - IResampler sampler = KnownResamplers.Triangle; - float result = sampler.GetValue(x); - - Assert.Equal(result, expected); - } - - [Theory] - [InlineData(-2, 0)] - [InlineData(-1, 0)] - [InlineData(0, 1)] - [InlineData(1, 0)] - [InlineData(2, 0)] - public static void Lanczos3WindowOscillatesCorrectly(float x, float expected) - { - IResampler sampler = KnownResamplers.Lanczos3; - float result = sampler.GetValue(x); - - Assert.Equal(result, expected); - } - - [Theory] - [InlineData(-4, 0)] - [InlineData(-2, 0)] - [InlineData(0, 1)] - [InlineData(2, 0)] - [InlineData(4, 0)] - public static void Lanczos5WindowOscillatesCorrectly(float x, float expected) - { - IResampler sampler = KnownResamplers.Lanczos5; - float result = sampler.GetValue(x); - - Assert.Equal(result, expected); - } } } \ No newline at end of file From 53892deeb7120c9c93e49a0ae621b12189971e53 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 14 Apr 2019 22:13:51 +0200 Subject: [PATCH 061/223] ResizeWindow refactor 4 --- .../Transforms/Resize/ResizeKernel.cs | 29 +++++++++------- .../Transforms/Resize/ResizeProcessor.cs | 17 +++++----- .../Transforms/Resize/ResizeWindow.cs | 33 +++++++++++-------- ...ResizeKernelMapTests.ReferenceKernelMap.cs | 2 +- .../Transforms/ResizeKernelMapTests.cs | 4 +-- 5 files changed, 48 insertions(+), 37 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs index f349634ac0..28b3d3a4dd 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs @@ -19,27 +19,27 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// Initializes a new instance of the struct. /// [MethodImpl(InliningOptions.ShortMethod)] - internal ResizeKernel(int left, float* bufferPtr, int length) + internal ResizeKernel(int startIndex, float* bufferPtr, int length) { - this.Left = left; + this.StartIndex = startIndex; this.bufferPtr = bufferPtr; this.Length = length; } /// - /// Gets the left index for the destination row + /// Gets the start index for the destination row. /// - public int Left { get; } + public int StartIndex { get; } /// - /// Gets the the length of the kernel + /// Gets the the length of the kernel. /// public int Length { get; } /// - /// Gets the span representing the portion of the that this window covers + /// Gets the span representing the portion of the that this window covers. /// - /// The + /// The . /// public Span Values { @@ -55,17 +55,22 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms [MethodImpl(InliningOptions.ShortMethod)] public Vector4 Convolve(Span rowSpan) { - ref float horizontalValues = ref Unsafe.AsRef(this.bufferPtr); - int left = this.Left; - ref Vector4 vecPtr = ref Unsafe.Add(ref MemoryMarshal.GetReference(rowSpan), left); + ref Vector4 vecPtr = ref Unsafe.Add(ref MemoryMarshal.GetReference(rowSpan), this.StartIndex); + + return this.ConvolveCore(ref vecPtr); + } + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ConvolveCore(ref Vector4 rowStartRef) + { + ref float horizontalValues = ref Unsafe.AsRef(this.bufferPtr); // Destination color components Vector4 result = Vector4.Zero; for (int i = 0; i < this.Length; i++) { float weight = Unsafe.Add(ref horizontalValues, i); - Vector4 v = Unsafe.Add(ref vecPtr, i); + Vector4 v = Unsafe.Add(ref rowStartRef, i); result += v * weight; } @@ -73,7 +78,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } /// - /// Copy the contents of altering + /// Copy the contents of altering /// to the value . /// internal ResizeKernel AlterLeftValue(int left) diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs index 38419b5dd6..73f9b4241e 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs @@ -197,7 +197,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms int startY = this.TargetRectangle.Y; int startX = this.TargetRectangle.X; - var workingRect = Rectangle.Intersect( + var destWorkingRect = Rectangle.Intersect( this.TargetRectangle, new Rectangle(0, 0, width, height)); @@ -208,7 +208,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms float heightFactor = sourceRectangle.Height / (float)this.TargetRectangle.Height; ParallelHelper.IterateRows( - workingRect, + destWorkingRect, configuration, rows => { @@ -219,7 +219,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms source.GetPixelRowSpan((int)(((y - startY) * heightFactor) + sourceY)); Span targetRow = destination.GetPixelRowSpan(y); - for (int x = workingRect.Left; x < workingRect.Right; x++) + for (int x = destWorkingRect.Left; x < destWorkingRect.Right; x++) { // X coordinates of source points targetRow[x] = sourceRow[(int)(((x - startX) * widthFactor) + sourceX)]; @@ -248,7 +248,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms this.horizontalKernelMap, this.verticalKernelMap, width, - workingRect, + destWorkingRect, startX)) using (IMemoryOwner tempBuffer = source.MemoryAllocator.Allocate(width)) { @@ -257,12 +257,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms // Now process the rows. Span tempColSpan = tempBuffer.GetSpan(); - for (int y = workingRect.Top; y < workingRect.Bottom; y++) + for (int y = destWorkingRect.Top; y < destWorkingRect.Bottom; y++) { // Ensure offsets are normalized for cropping and padding. ResizeKernel kernel = this.verticalKernelMap.GetKernel(y - startY); - if (kernel.Left + kernel.Length > resizeWindow.Bottom) + if (kernel.StartIndex + kernel.Length > resizeWindow.Bottom) { resizeWindow.Slide(); } @@ -271,10 +271,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms for (int x = 0; x < width; x++) { - Span firstPassColumn = resizeWindow.GetColumnSpan(x); + Span firstPassColumn = resizeWindow.GetColumnSpan(x, kernel.StartIndex); + ref Vector4 rowStartReference = ref MemoryMarshal.GetReference(firstPassColumn); // Destination color components - Unsafe.Add(ref tempRowBase, x) = kernel.Convolve(firstPassColumn); + Unsafe.Add(ref tempRowBase, x) = kernel.ConvolveCore(ref rowStartReference); } Span targetRowSpan = destination.GetPixelRowSpan(y); diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWindow.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWindow.cs index 05015dee20..ba5f9a9849 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWindow.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWindow.cs @@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms private readonly ResizeKernelMap verticalKernelMap; - private readonly Rectangle workingRectangle; + private readonly Rectangle destWorkingRect; private readonly int diameter; @@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms ResizeKernelMap horizontalKernelMap, ResizeKernelMap verticalKernelMap, int destWidth, - Rectangle workingRectangle, + Rectangle destWorkingRect, int startX) { this.configuration = configuration; @@ -54,7 +54,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms this.conversionModifiers = conversionModifiers; this.horizontalKernelMap = horizontalKernelMap; this.verticalKernelMap = verticalKernelMap; - this.workingRectangle = workingRectangle; + this.destWorkingRect = destWorkingRect; this.startX = startX; this.diameter = verticalKernelMap.MaxDiameter; @@ -65,9 +65,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms AllocationOptions.Clean); this.tempRowBuffer = configuration.MemoryAllocator.Allocate(this.sourceRectangle.Width); - this.Top = this.sourceRectangle.Top; + this.Top = 0; - this.Bottom = this.sourceRectangle.Bottom; + this.Bottom = this.sourceRectangle.Height; } public int Bottom { get; private set; } @@ -80,15 +80,25 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Span GetColumnSpan(int x) + public Span GetColumnSpan(int x, int startY) { - return this.buffer.GetRowSpan(x); + return this.buffer.GetRowSpan(x).Slice(startY); } public void Initialize() + { + this.Initialize(0, this.sourceRectangle.Height); + } + + public void Slide() + { + throw new InvalidOperationException("Shouldn't happen yet!"); + } + + private void Initialize(int top, int bottom) { Span tempRowSpan = this.tempRowBuffer.GetSpan(); - for (int y = 0; y < this.sourceRectangle.Height; y++) + for (int y = top; y < bottom; y++) { Span sourceRow = this.source.GetRowSpan(y); @@ -100,17 +110,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms ref Vector4 firstPassBaseRef = ref this.buffer.Span[y]; - for (int x = this.workingRectangle.Left; x < this.workingRectangle.Right; x++) + for (int x = this.destWorkingRect.Left; x < this.destWorkingRect.Right; x++) { ResizeKernel kernel = this.horizontalKernelMap.GetKernel(x - this.startX); Unsafe.Add(ref firstPassBaseRef, x * this.sourceRectangle.Height) = kernel.Convolve(tempRowSpan); } } } - - public void Slide() - { - throw new InvalidOperationException("Shouldn't happen yet!"); - } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.ReferenceKernelMap.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.ReferenceKernelMap.cs index 7d842c4e1e..2c5914253a 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.ReferenceKernelMap.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.ReferenceKernelMap.cs @@ -104,7 +104,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms public static implicit operator ReferenceKernel(ResizeKernel orig) { - return new ReferenceKernel(orig.Left, orig.Values.ToArray()); + return new ReferenceKernel(orig.StartIndex, orig.Values.ToArray()); } } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs index 9de8665869..1bd909e7ea 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs @@ -151,8 +151,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms referenceKernel.Length == kernel.Length, $"referenceKernel.Length != kernel.Length: {referenceKernel.Length} != {kernel.Length}"); Assert.True( - referenceKernel.Left == kernel.Left, - $"referenceKernel.Left != kernel.Left: {referenceKernel.Left} != {kernel.Left}"); + referenceKernel.Left == kernel.StartIndex, + $"referenceKernel.Left != kernel.Left: {referenceKernel.Left} != {kernel.StartIndex}"); float[] expectedValues = referenceKernel.Values; Span actualValues = kernel.Values; From 7be9d82941f9768ea44071bcbe5f2b93b6f86429 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 14 Apr 2019 23:29:43 +0200 Subject: [PATCH 062/223] basic sliding window implementation (semi-stable state) --- .../Transforms/Resize/ResizeKernel.cs | 9 +++---- .../Transforms/Resize/ResizeProcessor.cs | 3 +-- .../Transforms/Resize/ResizeWindow.cs | 27 ++++++++++++------- .../Processors/Transforms/ResizeTests.cs | 1 + 4 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs index 28b3d3a4dd..4606f482cd 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs @@ -55,13 +55,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms [MethodImpl(InliningOptions.ShortMethod)] public Vector4 Convolve(Span rowSpan) { - ref Vector4 vecPtr = ref Unsafe.Add(ref MemoryMarshal.GetReference(rowSpan), this.StartIndex); - - return this.ConvolveCore(ref vecPtr); + return this.ConvolveCore(rowSpan.Slice(this.StartIndex)); } [MethodImpl(InliningOptions.ShortMethod)] - public Vector4 ConvolveCore(ref Vector4 rowStartRef) + public Vector4 ConvolveCore(Span offsetedRowSpan) { ref float horizontalValues = ref Unsafe.AsRef(this.bufferPtr); // Destination color components @@ -70,7 +68,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms for (int i = 0; i < this.Length; i++) { float weight = Unsafe.Add(ref horizontalValues, i); - Vector4 v = Unsafe.Add(ref rowStartRef, i); + //Vector4 v = Unsafe.Add(ref rowStartRef, i); + Vector4 v = offsetedRowSpan[i]; result += v * weight; } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs index 73f9b4241e..abbe5908e7 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs @@ -272,10 +272,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms for (int x = 0; x < width; x++) { Span firstPassColumn = resizeWindow.GetColumnSpan(x, kernel.StartIndex); - ref Vector4 rowStartReference = ref MemoryMarshal.GetReference(firstPassColumn); // Destination color components - Unsafe.Add(ref tempRowBase, x) = kernel.ConvolveCore(ref rowStartReference); + Unsafe.Add(ref tempRowBase, x) = kernel.ConvolveCore(firstPassColumn); } Span targetRowSpan = destination.GetPixelRowSpan(y); diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWindow.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWindow.cs index ba5f9a9849..8221a9cc43 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWindow.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWindow.cs @@ -38,6 +38,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms private readonly int diameter; + private readonly int windowHeight; + public ResizeWindow( Configuration configuration, BufferArea source, @@ -59,15 +61,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms this.diameter = verticalKernelMap.MaxDiameter; + this.windowHeight = Math.Min(this.sourceRectangle.Height, 2 * this.diameter); + this.buffer = configuration.MemoryAllocator.Allocate2D( - this.sourceRectangle.Height, + this.windowHeight, destWidth, AllocationOptions.Clean); this.tempRowBuffer = configuration.MemoryAllocator.Allocate(this.sourceRectangle.Width); - - this.Top = 0; - - this.Bottom = this.sourceRectangle.Height; } public int Bottom { get; private set; } @@ -82,21 +82,26 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms [MethodImpl(MethodImplOptions.AggressiveInlining)] public Span GetColumnSpan(int x, int startY) { - return this.buffer.GetRowSpan(x).Slice(startY); + return this.buffer.GetRowSpan(x).Slice(startY - this.Top); } public void Initialize() { - this.Initialize(0, this.sourceRectangle.Height); + this.Initialize(0, this.windowHeight); } public void Slide() { - throw new InvalidOperationException("Shouldn't happen yet!"); + int top = this.Top + this.diameter; + int bottom = Math.Min(this.Bottom + this.diameter, this.sourceRectangle.Height); + this.Initialize(top, bottom); } private void Initialize(int top, int bottom) { + this.Top = top; + this.Bottom = bottom; + Span tempRowSpan = this.tempRowBuffer.GetSpan(); for (int y = top; y < bottom; y++) { @@ -108,12 +113,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms tempRowSpan, this.conversionModifiers); - ref Vector4 firstPassBaseRef = ref this.buffer.Span[y]; + //ref Vector4 firstPassBaseRef = ref this.buffer.Span[y - top]; + Span firstPassSpan = this.buffer.Span.Slice(y - top); for (int x = this.destWorkingRect.Left; x < this.destWorkingRect.Right; x++) { ResizeKernel kernel = this.horizontalKernelMap.GetKernel(x - this.startX); - Unsafe.Add(ref firstPassBaseRef, x * this.sourceRectangle.Height) = kernel.Convolve(tempRowSpan); + firstPassSpan[x * this.windowHeight] = kernel.Convolve(tempRowSpan); + //Unsafe.Add(ref firstPassBaseRef, x * this.sourceRectangle.Height) = kernel.Convolve(tempRowSpan); } } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs index 8c59fff26b..3d6f08a152 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs @@ -102,6 +102,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms // [WithBasicTestPatternImages(15, 12, PixelTypes.Rgba32, 2, 3, 1, 2)] means: // resizing: (15, 12) -> (10, 6) // kernel dimensions: (3, 4) + using (Image image = provider.GetImage()) { From 23350cd57644109b8e99042aacdd6e5a3131a052 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 14 Apr 2019 23:51:08 +0200 Subject: [PATCH 063/223] fix ResizeWithCropHeightMode --- .../Processing/Processors/Transforms/Resize/ResizeProcessor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs index abbe5908e7..9508d7c009 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs @@ -262,7 +262,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms // Ensure offsets are normalized for cropping and padding. ResizeKernel kernel = this.verticalKernelMap.GetKernel(y - startY); - if (kernel.StartIndex + kernel.Length > resizeWindow.Bottom) + while (kernel.StartIndex + kernel.Length > resizeWindow.Bottom) { resizeWindow.Slide(); } From 40d036678807e009c3b6e80b74b967d9010788d6 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 15 Apr 2019 01:24:22 +0200 Subject: [PATCH 064/223] reference output for Resize_BasicSmall --- tests/Images/External | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Images/External b/tests/Images/External index 94b5a8e11b..c7333d2a81 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 94b5a8e11b33ba62c15d1d03f1b8b721468764f1 +Subproject commit c7333d2a81a74d1936bd202bcb6b16cbfe6bcdce From 5108752e5cad94ad0883c2eb2bc16e79c2204394 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 15 Apr 2019 01:46:21 +0200 Subject: [PATCH 065/223] minor optimization --- .../Processors/Transforms/Resize/ResizeProcessor.cs | 4 +++- .../Processing/Processors/Transforms/Resize/ResizeWindow.cs | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs index 9508d7c009..d61cc5e89c 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs @@ -269,9 +269,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms ref Vector4 tempRowBase = ref MemoryMarshal.GetReference(tempColSpan); + int top = kernel.StartIndex - resizeWindow.Top; + for (int x = 0; x < width; x++) { - Span firstPassColumn = resizeWindow.GetColumnSpan(x, kernel.StartIndex); + Span firstPassColumn = resizeWindow.GetColumnSpan(x).Slice(top); // Destination color components Unsafe.Add(ref tempRowBase, x) = kernel.ConvolveCore(firstPassColumn); diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWindow.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWindow.cs index 8221a9cc43..afa9dac2d2 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWindow.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWindow.cs @@ -85,6 +85,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms return this.buffer.GetRowSpan(x).Slice(startY - this.Top); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span GetColumnSpan(int x) + { + return this.buffer.GetRowSpan(x); + } + public void Initialize() { this.Initialize(0, this.windowHeight); From 743e3519880a47bbd8cf981f82e61c5f098b0ffc Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 15 Apr 2019 18:02:39 +1000 Subject: [PATCH 066/223] Handle incorrect colorspace metadata. Fix #882 (#885) --- src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs | 13 ++++++------- .../Formats/Jpg/JpegDecoderTests.Images.cs | 5 ++++- tests/ImageSharp.Tests/TestImages.cs | 1 + tests/Images/External | 2 +- .../Jpg/issues/issue855-incorrect-colorspace.jpg | 3 +++ 5 files changed, 15 insertions(+), 9 deletions(-) create mode 100644 tests/Images/Input/Jpg/issues/issue855-incorrect-colorspace.jpg diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 9852b36e49..6d6983f191 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -401,15 +401,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg if (this.ComponentCount == 3) { - if (this.adobe.Equals(default) || this.adobe.ColorTransform == JpegConstants.Adobe.ColorTransformYCbCr) - { - return JpegColorSpace.YCbCr; - } - - if (this.adobe.ColorTransform == JpegConstants.Adobe.ColorTransformUnknown) + if (!this.adobe.Equals(default) && this.adobe.ColorTransform == JpegConstants.Adobe.ColorTransformUnknown) { return JpegColorSpace.RGB; } + + // Some images are poorly encoded and contain incorrect colorspace transform metadata. + // We ignore that and always fall back to the default colorspace. + return JpegColorSpace.YCbCr; } if (this.ComponentCount == 4) @@ -419,7 +418,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg : JpegColorSpace.Cmyk; } - JpegThrowHelper.ThrowImageFormatException($"Unsupported color mode. Max components 4; found {this.ComponentCount}"); + JpegThrowHelper.ThrowImageFormatException($"Unsupported color mode. Supported component counts 1, 3, and 4; found {this.ComponentCount}"); return default; } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs index 8a94826077..442fcb3d12 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs @@ -31,9 +31,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg TestImages.Jpeg.Issues.ExifGetString750Load, TestImages.Jpeg.Issues.ExifGetString750Transform, - // LibJpeg can open this despite the invalid desity units. + // LibJpeg can open this despite the invalid density units. TestImages.Jpeg.Issues.Fuzz.ArgumentOutOfRangeException825B, + // LibJpeg can open this despite incorrect colorspace metadata. + TestImages.Jpeg.Issues.IncorrectColorspace855, + // High depth images TestImages.Jpeg.Baseline.Testorig12bit, }; diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 1efb9e35fd..8b2fd2e7f8 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -175,6 +175,7 @@ namespace SixLabors.ImageSharp.Tests public const string ExifGetString750Transform = "Jpg/issues/issue750-exif-tranform.jpg"; public const string ExifGetString750Load = "Jpg/issues/issue750-exif-load.jpg"; public const string IncorrectQuality845 = "Jpg/issues/Issue845-Incorrect-Quality99.jpg"; + public const string IncorrectColorspace855 = "Jpg/issues/issue855-incorrect-colorspace.jpg"; public static class Fuzz { diff --git a/tests/Images/External b/tests/Images/External index 94b5a8e11b..802725dec2 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 94b5a8e11b33ba62c15d1d03f1b8b721468764f1 +Subproject commit 802725dec2a6b1ca02f9e2f9a4c3f625583d0696 diff --git a/tests/Images/Input/Jpg/issues/issue855-incorrect-colorspace.jpg b/tests/Images/Input/Jpg/issues/issue855-incorrect-colorspace.jpg new file mode 100644 index 0000000000..77c0327680 --- /dev/null +++ b/tests/Images/Input/Jpg/issues/issue855-incorrect-colorspace.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fabf628f032dce427a6a0e0f252404d66dc4ce2cd7ee2d7ec72a1fbe79c625d0 +size 142656 From ec1971435b55510355f2dbf14891b69563b581c1 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 18 Apr 2019 23:43:44 +0200 Subject: [PATCH 067/223] refactor - ResizeWindow -> ResizeWorker - Most logic moved to ResizeWorker --- .../Transforms/Resize/ResizeProcessor.cs | 42 ++--------- .../{ResizeWindow.cs => ResizeWorker.cs} | 72 +++++++++++++++---- 2 files changed, 62 insertions(+), 52 deletions(-) rename src/ImageSharp/Processing/Processors/Transforms/Resize/{ResizeWindow.cs => ResizeWorker.cs} (61%) diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs index d61cc5e89c..63dd5efca0 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs @@ -237,11 +237,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms BufferArea sourceArea = source.PixelBuffer.GetArea(sourceRectangle); - // Interpolate the image using the calculated weights. - // A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm - // First process the columns. Since we are not using multiple threads startY and endY - // are the upper and lower bounds of the source rectangle. - using (var resizeWindow = new ResizeWindow( + // If we want to reintroduce processing: + // it's possible to launch multiple workers for different regions of the image + using (var worker = new ResizeWorker( configuration, sourceArea, conversionModifiers, @@ -250,39 +248,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms width, destWorkingRect, startX)) - using (IMemoryOwner tempBuffer = source.MemoryAllocator.Allocate(width)) { - resizeWindow.Initialize(); - - // Now process the rows. - Span tempColSpan = tempBuffer.GetSpan(); - - for (int y = destWorkingRect.Top; y < destWorkingRect.Bottom; y++) - { - // Ensure offsets are normalized for cropping and padding. - ResizeKernel kernel = this.verticalKernelMap.GetKernel(y - startY); - - while (kernel.StartIndex + kernel.Length > resizeWindow.Bottom) - { - resizeWindow.Slide(); - } - - ref Vector4 tempRowBase = ref MemoryMarshal.GetReference(tempColSpan); - - int top = kernel.StartIndex - resizeWindow.Top; - - for (int x = 0; x < width; x++) - { - Span firstPassColumn = resizeWindow.GetColumnSpan(x).Slice(top); - - // Destination color components - Unsafe.Add(ref tempRowBase, x) = kernel.ConvolveCore(firstPassColumn); - } - - Span targetRowSpan = destination.GetPixelRowSpan(y); - - PixelOperations.Instance.FromVector4Destructive(configuration, tempColSpan, targetRowSpan, conversionModifiers); - } + worker.Initialize(); + worker.FillDestinationPixels(destWorkingRect.Top, destWorkingRect.Bottom, startY, destination.PixelBuffer); } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWindow.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs similarity index 61% rename from src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWindow.cs rename to src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs index afa9dac2d2..11b49b8631 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWindow.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs @@ -5,6 +5,7 @@ using System; using System.Buffers; using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -13,7 +14,7 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { - internal class ResizeWindow : IDisposable + internal class ResizeWorker : IDisposable where TPixel : struct, IPixel { private readonly Buffer2D buffer; @@ -32,15 +33,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms private readonly IMemoryOwner tempRowBuffer; + private readonly IMemoryOwner tempColumnBuffer; + private readonly ResizeKernelMap verticalKernelMap; + private readonly int destWidth; + private readonly Rectangle destWorkingRect; private readonly int diameter; private readonly int windowHeight; - public ResizeWindow( + public ResizeWorker( Configuration configuration, BufferArea source, PixelConversionModifiers conversionModifiers, @@ -56,6 +61,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms this.conversionModifiers = conversionModifiers; this.horizontalKernelMap = horizontalKernelMap; this.verticalKernelMap = verticalKernelMap; + this.destWidth = destWidth; this.destWorkingRect = destWorkingRect; this.startX = startX; @@ -67,22 +73,29 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms this.windowHeight, destWidth, AllocationOptions.Clean); + this.tempRowBuffer = configuration.MemoryAllocator.Allocate(this.sourceRectangle.Width); + this.tempColumnBuffer = configuration.MemoryAllocator.Allocate(destWidth); + + this.CurrentMinY = 0; + this.CurrentMaxY = this.windowHeight; } - public int Bottom { get; private set; } + public int CurrentMaxY { get; private set; } - public int Top { get; private set; } + public int CurrentMinY { get; private set; } public void Dispose() { this.buffer.Dispose(); + this.tempRowBuffer.Dispose(); + this.tempColumnBuffer.Dispose(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Span GetColumnSpan(int x, int startY) { - return this.buffer.GetRowSpan(x).Slice(startY - this.Top); + return this.buffer.GetRowSpan(x).Slice(startY - this.CurrentMinY); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -93,23 +106,52 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public void Initialize() { - this.Initialize(0, this.windowHeight); + this.CalculateFirstPassValues(0, this.windowHeight); } - public void Slide() + public void FillDestinationPixels(int minY, int maxY, int startY, Buffer2D destination) { - int top = this.Top + this.diameter; - int bottom = Math.Min(this.Bottom + this.diameter, this.sourceRectangle.Height); - this.Initialize(top, bottom); + Span tempColSpan = this.tempColumnBuffer.GetSpan(); + + for (int y = minY; y < maxY; y++) + { + // Ensure offsets are normalized for cropping and padding. + ResizeKernel kernel = this.verticalKernelMap.GetKernel(y - startY); + + while (kernel.StartIndex + kernel.Length > this.CurrentMaxY) + { + this.Slide(); + } + + ref Vector4 tempRowBase = ref MemoryMarshal.GetReference(tempColSpan); + + int top = kernel.StartIndex - this.CurrentMinY; + + for (int x = 0; x < this.destWidth; x++) + { + Span firstPassColumn = this.GetColumnSpan(x).Slice(top); + + // Destination color components + Unsafe.Add(ref tempRowBase, x) = kernel.ConvolveCore(firstPassColumn); + } + + Span targetRowSpan = destination.GetRowSpan(y); + + PixelOperations.Instance.FromVector4Destructive(configuration, tempColSpan, targetRowSpan, conversionModifiers); + } } - private void Initialize(int top, int bottom) + public void Slide() { - this.Top = top; - this.Bottom = bottom; + this.CurrentMinY = this.CurrentMinY + this.diameter; + this.CurrentMaxY = Math.Min(this.CurrentMaxY + this.diameter, this.sourceRectangle.Height); + this.CalculateFirstPassValues(this.CurrentMinY, this.CurrentMaxY); + } + private void CalculateFirstPassValues(int minY, int maxY) + { Span tempRowSpan = this.tempRowBuffer.GetSpan(); - for (int y = top; y < bottom; y++) + for (int y = minY; y < maxY; y++) { Span sourceRow = this.source.GetRowSpan(y); @@ -120,7 +162,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms this.conversionModifiers); //ref Vector4 firstPassBaseRef = ref this.buffer.Span[y - top]; - Span firstPassSpan = this.buffer.Span.Slice(y - top); + Span firstPassSpan = this.buffer.Span.Slice(y - minY); for (int x = this.destWorkingRect.Left; x < this.destWorkingRect.Right; x++) { From 07808bf8eb80c4a9d4b5531ba003ebb681667494 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 19 Apr 2019 00:23:06 +0200 Subject: [PATCH 068/223] refactor stuff + implement CalculateResizeWorkerWindowCount() --- src/ImageSharp/Configuration.cs | 13 +- .../Transforms/Resize}/ResizeHelper.cs | 339 ++++++++++-------- tests/ImageSharp.Tests/ConfigurationTests.cs | 17 +- .../Processors/Transforms/ResizeTests.cs | 19 + 4 files changed, 232 insertions(+), 156 deletions(-) rename src/ImageSharp/Processing/{ => Processors/Transforms/Resize}/ResizeHelper.cs (88%) diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index c0064d1877..b1767101d4 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -10,6 +10,7 @@ using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Transforms; using SixLabors.Memory; namespace SixLabors.ImageSharp @@ -102,6 +103,15 @@ namespace SixLabors.ImageSharp /// internal IFileSystem FileSystem { get; set; } = new LocalFileSystem(); + /// + /// Gets or sets the working buffer size hint for image processors. + /// The default value is 1MB. + /// + /// + /// Currently only used by . + /// + internal int WorkingBufferSizeHintInBytes { get; set; } = 1 * 1024 * 1024; + /// /// Gets or sets the image operations provider factory. /// @@ -130,7 +140,8 @@ namespace SixLabors.ImageSharp MemoryAllocator = this.MemoryAllocator, ImageOperationsProvider = this.ImageOperationsProvider, ReadOrigin = this.ReadOrigin, - FileSystem = this.FileSystem + FileSystem = this.FileSystem, + WorkingBufferSizeHintInBytes = this.WorkingBufferSizeHintInBytes, }; } diff --git a/src/ImageSharp/Processing/ResizeHelper.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs similarity index 88% rename from src/ImageSharp/Processing/ResizeHelper.cs rename to src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs index 3ae632162f..0bc8cfda02 100644 --- a/src/ImageSharp/Processing/ResizeHelper.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs @@ -1,11 +1,13 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; using System.Linq; +using System.Numerics; + using SixLabors.Primitives; -namespace SixLabors.ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing.Processors.Transforms { /// /// Provides methods to help calculate the target rectangle when resizing using the @@ -13,6 +15,16 @@ namespace SixLabors.ImageSharp.Processing /// internal static class ResizeHelper { + public static unsafe int CalculateResizeWorkerWindowCount( + int windowDiameter, + int width, + int sizeLimitHintInBytes) + { + int sizeLimitHint = sizeLimitHintInBytes / sizeof(Vector4); + int sizeOfOneWindow = windowDiameter * width; + return Math.Max(2, sizeLimitHint / sizeOfOneWindow); + } + /// /// Calculates the target location and bounds to perform the resize operation against. /// @@ -21,9 +33,13 @@ namespace SixLabors.ImageSharp.Processing /// The target width /// The target height /// - /// The . + /// The tuple representing the location and the bounds /// - public static (Size, Rectangle) CalculateTargetLocationAndBounds(Size sourceSize, ResizeOptions options, int width, int height) + public static (Size, Rectangle) CalculateTargetLocationAndBounds( + Size sourceSize, + ResizeOptions options, + int width, + int height) { switch (options.Mode) { @@ -44,7 +60,90 @@ namespace SixLabors.ImageSharp.Processing } } - private static (Size, Rectangle) CalculateCropRectangle(Size source, ResizeOptions options, int width, int height) + private static (Size, Rectangle) CalculateBoxPadRectangle( + Size source, + ResizeOptions options, + int width, + int height) + { + if (width <= 0 || height <= 0) + { + return (new Size(source.Width, source.Height), new Rectangle(0, 0, source.Width, source.Height)); + } + + int sourceWidth = source.Width; + int sourceHeight = source.Height; + + // Fractional variants for preserving aspect ratio. + float percentHeight = MathF.Abs(height / (float)sourceHeight); + float percentWidth = MathF.Abs(width / (float)sourceWidth); + + int boxPadHeight = height > 0 ? height : (int)MathF.Round(sourceHeight * percentWidth); + int boxPadWidth = width > 0 ? width : (int)MathF.Round(sourceWidth * percentHeight); + + // Only calculate if upscaling. + if (sourceWidth < boxPadWidth && sourceHeight < boxPadHeight) + { + int destinationX; + int destinationY; + int destinationWidth = sourceWidth; + int destinationHeight = sourceHeight; + width = boxPadWidth; + height = boxPadHeight; + + switch (options.Position) + { + case AnchorPositionMode.Left: + destinationY = (height - sourceHeight) / 2; + destinationX = 0; + break; + case AnchorPositionMode.Right: + destinationY = (height - sourceHeight) / 2; + destinationX = width - sourceWidth; + break; + case AnchorPositionMode.TopRight: + destinationY = 0; + destinationX = width - sourceWidth; + break; + case AnchorPositionMode.Top: + destinationY = 0; + destinationX = (width - sourceWidth) / 2; + break; + case AnchorPositionMode.TopLeft: + destinationY = 0; + destinationX = 0; + break; + case AnchorPositionMode.BottomRight: + destinationY = height - sourceHeight; + destinationX = width - sourceWidth; + break; + case AnchorPositionMode.Bottom: + destinationY = height - sourceHeight; + destinationX = (width - sourceWidth) / 2; + break; + case AnchorPositionMode.BottomLeft: + destinationY = height - sourceHeight; + destinationX = 0; + break; + default: + destinationY = (height - sourceHeight) / 2; + destinationX = (width - sourceWidth) / 2; + break; + } + + return (new Size(width, height), + new Rectangle(destinationX, destinationY, destinationWidth, destinationHeight)); + } + + // Switch to pad mode to downscale and calculate from there. + return CalculatePadRectangle(source, options, width, height); + } + + private static (Size, Rectangle) CalculateCropRectangle( + Size source, + ResizeOptions options, + int width, + int height) { if (width <= 0 || height <= 0) { @@ -147,152 +246,15 @@ namespace SixLabors.ImageSharp.Processing destinationWidth = (int)MathF.Ceiling(sourceWidth * percentHeight); } - return (new Size(width, height), new Rectangle(destinationX, destinationY, destinationWidth, destinationHeight)); - } - - private static (Size, Rectangle) CalculatePadRectangle(Size source, ResizeOptions options, int width, int height) - { - if (width <= 0 || height <= 0) - { - return (new Size(source.Width, source.Height), new Rectangle(0, 0, source.Width, source.Height)); - } - - float ratio; - int sourceWidth = source.Width; - int sourceHeight = source.Height; - - int destinationX = 0; - int destinationY = 0; - int destinationWidth = width; - int destinationHeight = height; - - // Fractional variants for preserving aspect ratio. - float percentHeight = MathF.Abs(height / (float)sourceHeight); - float percentWidth = MathF.Abs(width / (float)sourceWidth); - - if (percentHeight < percentWidth) - { - ratio = percentHeight; - destinationWidth = (int)MathF.Round(sourceWidth * percentHeight); - - switch (options.Position) - { - case AnchorPositionMode.Left: - case AnchorPositionMode.TopLeft: - case AnchorPositionMode.BottomLeft: - destinationX = 0; - break; - case AnchorPositionMode.Right: - case AnchorPositionMode.TopRight: - case AnchorPositionMode.BottomRight: - destinationX = (int)MathF.Round(width - (sourceWidth * ratio)); - break; - default: - destinationX = (int)MathF.Round((width - (sourceWidth * ratio)) / 2F); - break; - } - } - else - { - ratio = percentWidth; - destinationHeight = (int)MathF.Round(sourceHeight * percentWidth); - - switch (options.Position) - { - case AnchorPositionMode.Top: - case AnchorPositionMode.TopLeft: - case AnchorPositionMode.TopRight: - destinationY = 0; - break; - case AnchorPositionMode.Bottom: - case AnchorPositionMode.BottomLeft: - case AnchorPositionMode.BottomRight: - destinationY = (int)MathF.Round(height - (sourceHeight * ratio)); - break; - default: - destinationY = (int)MathF.Round((height - (sourceHeight * ratio)) / 2F); - break; - } - } - - return (new Size(width, height), new Rectangle(destinationX, destinationY, destinationWidth, destinationHeight)); - } - - private static (Size, Rectangle) CalculateBoxPadRectangle(Size source, ResizeOptions options, int width, int height) - { - if (width <= 0 || height <= 0) - { - return (new Size(source.Width, source.Height), new Rectangle(0, 0, source.Width, source.Height)); - } - - int sourceWidth = source.Width; - int sourceHeight = source.Height; - - // Fractional variants for preserving aspect ratio. - float percentHeight = MathF.Abs(height / (float)sourceHeight); - float percentWidth = MathF.Abs(width / (float)sourceWidth); - - int boxPadHeight = height > 0 ? height : (int)MathF.Round(sourceHeight * percentWidth); - int boxPadWidth = width > 0 ? width : (int)MathF.Round(sourceWidth * percentHeight); - - // Only calculate if upscaling. - if (sourceWidth < boxPadWidth && sourceHeight < boxPadHeight) - { - int destinationX; - int destinationY; - int destinationWidth = sourceWidth; - int destinationHeight = sourceHeight; - width = boxPadWidth; - height = boxPadHeight; - - switch (options.Position) - { - case AnchorPositionMode.Left: - destinationY = (height - sourceHeight) / 2; - destinationX = 0; - break; - case AnchorPositionMode.Right: - destinationY = (height - sourceHeight) / 2; - destinationX = width - sourceWidth; - break; - case AnchorPositionMode.TopRight: - destinationY = 0; - destinationX = width - sourceWidth; - break; - case AnchorPositionMode.Top: - destinationY = 0; - destinationX = (width - sourceWidth) / 2; - break; - case AnchorPositionMode.TopLeft: - destinationY = 0; - destinationX = 0; - break; - case AnchorPositionMode.BottomRight: - destinationY = height - sourceHeight; - destinationX = width - sourceWidth; - break; - case AnchorPositionMode.Bottom: - destinationY = height - sourceHeight; - destinationX = (width - sourceWidth) / 2; - break; - case AnchorPositionMode.BottomLeft: - destinationY = height - sourceHeight; - destinationX = 0; - break; - default: - destinationY = (height - sourceHeight) / 2; - destinationX = (width - sourceWidth) / 2; - break; - } - - return (new Size(width, height), new Rectangle(destinationX, destinationY, destinationWidth, destinationHeight)); - } - - // Switch to pad mode to downscale and calculate from there. - return CalculatePadRectangle(source, options, width, height); + return (new Size(width, height), + new Rectangle(destinationX, destinationY, destinationWidth, destinationHeight)); } - private static (Size, Rectangle) CalculateMaxRectangle(Size source, ResizeOptions options, int width, int height) + private static (Size, Rectangle) CalculateMaxRectangle( + Size source, + ResizeOptions options, + int width, + int height) { int destinationWidth = width; int destinationHeight = height; @@ -320,7 +282,11 @@ namespace SixLabors.ImageSharp.Processing return (new Size(width, height), new Rectangle(0, 0, destinationWidth, destinationHeight)); } - private static (Size, Rectangle) CalculateMinRectangle(Size source, ResizeOptions options, int width, int height) + private static (Size, Rectangle) CalculateMinRectangle( + Size source, + ResizeOptions options, + int width, + int height) { int sourceWidth = source.Width; int sourceHeight = source.Height; @@ -372,5 +338,78 @@ namespace SixLabors.ImageSharp.Processing // Replace the size to match the rectangle. return (new Size(width, height), new Rectangle(0, 0, destinationWidth, destinationHeight)); } + + private static (Size, Rectangle) CalculatePadRectangle( + Size source, + ResizeOptions options, + int width, + int height) + { + if (width <= 0 || height <= 0) + { + return (new Size(source.Width, source.Height), new Rectangle(0, 0, source.Width, source.Height)); + } + + float ratio; + int sourceWidth = source.Width; + int sourceHeight = source.Height; + + int destinationX = 0; + int destinationY = 0; + int destinationWidth = width; + int destinationHeight = height; + + // Fractional variants for preserving aspect ratio. + float percentHeight = MathF.Abs(height / (float)sourceHeight); + float percentWidth = MathF.Abs(width / (float)sourceWidth); + + if (percentHeight < percentWidth) + { + ratio = percentHeight; + destinationWidth = (int)MathF.Round(sourceWidth * percentHeight); + + switch (options.Position) + { + case AnchorPositionMode.Left: + case AnchorPositionMode.TopLeft: + case AnchorPositionMode.BottomLeft: + destinationX = 0; + break; + case AnchorPositionMode.Right: + case AnchorPositionMode.TopRight: + case AnchorPositionMode.BottomRight: + destinationX = (int)MathF.Round(width - (sourceWidth * ratio)); + break; + default: + destinationX = (int)MathF.Round((width - (sourceWidth * ratio)) / 2F); + break; + } + } + else + { + ratio = percentWidth; + destinationHeight = (int)MathF.Round(sourceHeight * percentWidth); + + switch (options.Position) + { + case AnchorPositionMode.Top: + case AnchorPositionMode.TopLeft: + case AnchorPositionMode.TopRight: + destinationY = 0; + break; + case AnchorPositionMode.Bottom: + case AnchorPositionMode.BottomLeft: + case AnchorPositionMode.BottomRight: + destinationY = (int)MathF.Round(height - (sourceHeight * ratio)); + break; + default: + destinationY = (int)MathF.Round((height - (sourceHeight * ratio)) / 2F); + break; + } + } + + return (new Size(width, height), + new Rectangle(destinationX, destinationY, destinationWidth, destinationHeight)); + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/ConfigurationTests.cs b/tests/ImageSharp.Tests/ConfigurationTests.cs index 208387e6d1..6f68d04288 100644 --- a/tests/ImageSharp.Tests/ConfigurationTests.cs +++ b/tests/ImageSharp.Tests/ConfigurationTests.cs @@ -39,13 +39,13 @@ namespace SixLabors.ImageSharp.Tests /// Test that the default configuration is not null. /// [Fact] - public void TestDefaultConfigurationIsNotNull() => Assert.True(Configuration.Default != null); + public void TestDefaultConfigurationIsNotNull() => Assert.True(this.DefaultConfiguration != null); /// /// Test that the default configuration read origin options is set to begin. /// [Fact] - public void TestDefaultConfigurationReadOriginIsCurrent() => Assert.True(Configuration.Default.ReadOrigin == ReadOrigin.Current); + public void TestDefaultConfigurationReadOriginIsCurrent() => Assert.True(this.DefaultConfiguration.ReadOrigin == ReadOrigin.Current); /// /// Test that the default configuration parallel options max degrees of parallelism matches the @@ -54,7 +54,7 @@ namespace SixLabors.ImageSharp.Tests [Fact] public void TestDefaultConfigurationMaxDegreeOfParallelism() { - Assert.True(Configuration.Default.MaxDegreeOfParallelism == Environment.ProcessorCount); + Assert.True(this.DefaultConfiguration.MaxDegreeOfParallelism == Environment.ProcessorCount); var cfg = new Configuration(); Assert.True(cfg.MaxDegreeOfParallelism == Environment.ProcessorCount); @@ -93,7 +93,7 @@ namespace SixLabors.ImageSharp.Tests public void ConfigurationCannotAddDuplicates() { const int count = 4; - Configuration config = Configuration.Default; + Configuration config = this.DefaultConfiguration; Assert.Equal(count, config.ImageFormats.Count()); @@ -105,9 +105,16 @@ namespace SixLabors.ImageSharp.Tests [Fact] public void DefaultConfigurationHasCorrectFormatCount() { - Configuration config = Configuration.Default; + Configuration config = Configuration.CreateDefaultInstance(); Assert.Equal(4, config.ImageFormats.Count()); } + + [Fact] + public void WorkingBufferSizeHint_DefaultIsCorrect() + { + Configuration config = this.DefaultConfiguration; + Assert.True(config.WorkingBufferSizeHintInBytes > 1024); + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs index 3d6f08a152..ec0092f340 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs @@ -35,6 +35,25 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.07F); + [Theory] + [InlineData(20, 100, 1, 2)] + [InlineData(20, 100, 20*100*16, 2)] + [InlineData(20, 100, 40*100*16, 2)] + [InlineData(20, 100, 59*100*16, 2)] + [InlineData(20, 100, 60*100*16, 3)] + [InlineData(17, 63, 5*17*63*16, 5)] + [InlineData(17, 63, 5*17*63*16+1, 5)] + [InlineData(17, 63, 6*17*63*16-1, 5)] + public void CalculateResizeWorkerWindowCount( + int windowDiameter, + int width, + int sizeLimitHintInBytes, + int expectedCount) + { + int actualCount = ResizeHelper.CalculateResizeWorkerWindowCount(windowDiameter, width, sizeLimitHintInBytes); + Assert.Equal(expectedCount, actualCount); + } + [Theory] [InlineData(-2, 0)] [InlineData(-1, 0)] From 2859fdd772d3128b3e4de2e651367499f6babd92 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 19 Apr 2019 15:02:58 +0200 Subject: [PATCH 069/223] utilize CalculateResizeWorkerHeightInWindowBands() which has been renamed from CalculateResizeWorkerWindowCount() --- src/ImageSharp/Configuration.cs | 14 +++++++------- .../Processors/Transforms/Resize/ResizeHelper.cs | 6 +++--- .../Processors/Transforms/Resize/ResizeWorker.cs | 15 ++++++++++----- .../Processors/Transforms/ResizeKernelMapTests.cs | 1 + .../Processors/Transforms/ResizeTests.cs | 7 +++++-- 5 files changed, 26 insertions(+), 17 deletions(-) diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index b1767101d4..7cb014563d 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -128,9 +128,9 @@ namespace SixLabors.ImageSharp } /// - /// Creates a shallow copy of the + /// Creates a shallow copy of the . /// - /// A new configuration instance + /// A new configuration instance. public Configuration Clone() { return new Configuration @@ -147,12 +147,12 @@ namespace SixLabors.ImageSharp /// /// Creates the default instance with the following s preregistered: - /// - /// - /// - /// + /// + /// + /// + /// . /// - /// The default configuration of + /// The default configuration of . internal static Configuration CreateDefaultInstance() { return new Configuration( diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs index 0bc8cfda02..595a7e852e 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs @@ -15,13 +15,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// internal static class ResizeHelper { - public static unsafe int CalculateResizeWorkerWindowCount( - int windowDiameter, + public static unsafe int CalculateResizeWorkerHeightInWindowBands( + int windowBandDiameter, int width, int sizeLimitHintInBytes) { int sizeLimitHint = sizeLimitHintInBytes / sizeof(Vector4); - int sizeOfOneWindow = windowDiameter * width; + int sizeOfOneWindow = windowBandDiameter * width; return Math.Max(2, sizeLimitHint / sizeOfOneWindow); } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs index 11b49b8631..73dcce58b6 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs @@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms private readonly Rectangle destWorkingRect; - private readonly int diameter; + private readonly int windowBandDiameter; private readonly int windowHeight; @@ -65,9 +65,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms this.destWorkingRect = destWorkingRect; this.startX = startX; - this.diameter = verticalKernelMap.MaxDiameter; + this.windowBandDiameter = verticalKernelMap.MaxDiameter; - this.windowHeight = Math.Min(this.sourceRectangle.Height, 2 * this.diameter); + int numberOfWindowBands = ResizeHelper.CalculateResizeWorkerHeightInWindowBands( + this.windowBandDiameter, + destWidth, + configuration.WorkingBufferSizeHintInBytes); + + this.windowHeight = Math.Min(this.sourceRectangle.Height, numberOfWindowBands * this.windowBandDiameter); this.buffer = configuration.MemoryAllocator.Allocate2D( this.windowHeight, @@ -143,8 +148,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public void Slide() { - this.CurrentMinY = this.CurrentMinY + this.diameter; - this.CurrentMaxY = Math.Min(this.CurrentMaxY + this.diameter, this.sourceRectangle.Height); + this.CurrentMinY = this.CurrentMinY + this.windowBandDiameter; + this.CurrentMaxY = Math.Min(this.CurrentMaxY + this.windowBandDiameter, this.sourceRectangle.Height); this.CalculateFirstPassValues(this.CurrentMinY, this.CurrentMaxY); } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs index 1bd909e7ea..51680eee04 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs @@ -36,6 +36,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms { nameof(KnownResamplers.Bicubic), 40, 50 }, { nameof(KnownResamplers.Bicubic), 500, 200 }, { nameof(KnownResamplers.Bicubic), 200, 500 }, + { nameof(KnownResamplers.Bicubic), 3032, 400 }, { nameof(KnownResamplers.Bicubic), 10, 25 }, diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs index ec0092f340..5325581662 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs @@ -44,13 +44,16 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms [InlineData(17, 63, 5*17*63*16, 5)] [InlineData(17, 63, 5*17*63*16+1, 5)] [InlineData(17, 63, 6*17*63*16-1, 5)] - public void CalculateResizeWorkerWindowCount( + [InlineData(33, 400, 1*1024*1024, 4)] + [InlineData(33, 400, 8*1024*1024, 39)] + [InlineData(50, 300, 1*1024*1024, 4)] + public void CalculateResizeWorkerHeightInWindowBands( int windowDiameter, int width, int sizeLimitHintInBytes, int expectedCount) { - int actualCount = ResizeHelper.CalculateResizeWorkerWindowCount(windowDiameter, width, sizeLimitHintInBytes); + int actualCount = ResizeHelper.CalculateResizeWorkerHeightInWindowBands(windowDiameter, width, sizeLimitHintInBytes); Assert.Equal(expectedCount, actualCount); } From 7f0a245b6ac6241c0d404bcf336bc12970ef3c30 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 19 Apr 2019 16:23:01 +0200 Subject: [PATCH 070/223] improve benchmark: ArrayCopy -> CopyBuffers --- .../Transforms/Resize/ResizeKernel.cs | 4 +- .../Transforms/Resize/ResizeWorker.cs | 7 +- .../General/ArrayCopy.cs | 103 ---------- .../General/CopyBuffers.cs | 184 ++++++++++++++++++ 4 files changed, 191 insertions(+), 107 deletions(-) delete mode 100644 tests/ImageSharp.Benchmarks/General/ArrayCopy.cs create mode 100644 tests/ImageSharp.Benchmarks/General/CopyBuffers.cs diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs index 4606f482cd..71cba90564 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs @@ -62,13 +62,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public Vector4 ConvolveCore(Span offsetedRowSpan) { ref float horizontalValues = ref Unsafe.AsRef(this.bufferPtr); + // Destination color components Vector4 result = Vector4.Zero; for (int i = 0; i < this.Length; i++) { float weight = Unsafe.Add(ref horizontalValues, i); - //Vector4 v = Unsafe.Add(ref rowStartRef, i); + + // Vector4 v = Unsafe.Add(ref rowStartRef, i); Vector4 v = offsetedRowSpan[i]; result += v * weight; } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs index 73dcce58b6..e9e39ef13b 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs @@ -142,7 +142,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms Span targetRowSpan = destination.GetRowSpan(y); - PixelOperations.Instance.FromVector4Destructive(configuration, tempColSpan, targetRowSpan, conversionModifiers); + PixelOperations.Instance.FromVector4Destructive(this.configuration, tempColSpan, targetRowSpan, this.conversionModifiers); } } @@ -166,14 +166,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms tempRowSpan, this.conversionModifiers); - //ref Vector4 firstPassBaseRef = ref this.buffer.Span[y - top]; + // ref Vector4 firstPassBaseRef = ref this.buffer.Span[y - top]; Span firstPassSpan = this.buffer.Span.Slice(y - minY); for (int x = this.destWorkingRect.Left; x < this.destWorkingRect.Right; x++) { ResizeKernel kernel = this.horizontalKernelMap.GetKernel(x - this.startX); firstPassSpan[x * this.windowHeight] = kernel.Convolve(tempRowSpan); - //Unsafe.Add(ref firstPassBaseRef, x * this.sourceRectangle.Height) = kernel.Convolve(tempRowSpan); + + // Unsafe.Add(ref firstPassBaseRef, x * this.sourceRectangle.Height) = kernel.Convolve(tempRowSpan); } } } diff --git a/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs b/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs deleted file mode 100644 index ac6b3f93c7..0000000000 --- a/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs +++ /dev/null @@ -1,103 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// -namespace SixLabors.ImageSharp.Benchmarks.General -{ - using System; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - - using BenchmarkDotNet.Attributes; - - [Config(typeof(Config.ShortClr))] - public class ArrayCopy - { - [Params(10, 100, 1000, 10000)] - public int Count { get; set; } - - byte[] source; - - byte[] destination; - - [GlobalSetup] - public void SetUp() - { - this.source = new byte[this.Count]; - this.destination = new byte[this.Count]; - } - - [Benchmark(Baseline = true, Description = "Copy using Array.Copy()")] - public void CopyArray() - { - Array.Copy(this.source, this.destination, this.Count); - } - - [Benchmark(Description = "Copy using Unsafe")] - public unsafe void CopyUnsafe() - { - fixed (byte* pinnedDestination = this.destination) - fixed (byte* pinnedSource = this.source) - { - Unsafe.CopyBlock(pinnedSource, pinnedDestination, (uint)this.Count); - } - } - - [Benchmark(Description = "Copy using Buffer.BlockCopy()")] - public void CopyUsingBufferBlockCopy() - { - Buffer.BlockCopy(this.source, 0, this.destination, 0, this.Count); - } - - [Benchmark(Description = "Copy using Buffer.MemoryCopy")] - public unsafe void CopyUsingBufferMemoryCopy() - { - fixed (byte* pinnedDestination = this.destination) - fixed (byte* pinnedSource = this.source) - { - Buffer.MemoryCopy(pinnedSource, pinnedDestination, this.Count, this.Count); - } - } - - [Benchmark(Description = "Copy using Marshal.Copy")] - public unsafe void CopyUsingMarshalCopy() - { - fixed (byte* pinnedDestination = this.destination) - { - Marshal.Copy(this.source, 0, (IntPtr)pinnedDestination, this.Count); - } - } - - /***************************************************************************************************************** - *************** RESULTS on i7-4810MQ 2.80GHz + Clr 4.0.30319.42000, 64bit RyuJIT-v4.6.1085.0 ******************** - ***************************************************************************************************************** - * - * Method | Count | Mean | StdErr | StdDev | Scaled | Scaled-StdDev | - * ---------------------------------- |------ |------------ |----------- |----------- |------- |-------------- | - * 'Copy using Array.Copy()' | 10 | 20.3074 ns | 0.1194 ns | 0.2068 ns | 1.00 | 0.00 | - * 'Copy using Unsafe' | 10 | 6.1002 ns | 0.1981 ns | 0.3432 ns | 0.30 | 0.01 | - * 'Copy using Buffer.BlockCopy()' | 10 | 10.7879 ns | 0.0984 ns | 0.1705 ns | 0.53 | 0.01 | - * 'Copy using Buffer.MemoryCopy' | 10 | 4.9625 ns | 0.0200 ns | 0.0347 ns | 0.24 | 0.00 | - * 'Copy using Marshal.Copy' | 10 | 16.1782 ns | 0.0919 ns | 0.1592 ns | 0.80 | 0.01 | - * - * 'Copy using Array.Copy()' | 100 | 31.5945 ns | 0.2908 ns | 0.5037 ns | 1.00 | 0.00 | - * 'Copy using Unsafe' | 100 | 10.2722 ns | 0.5202 ns | 0.9010 ns | 0.33 | 0.02 | - * 'Copy using Buffer.BlockCopy()' | 100 | 22.0322 ns | 0.0284 ns | 0.0493 ns | 0.70 | 0.01 | - * 'Copy using Buffer.MemoryCopy' | 100 | 10.2472 ns | 0.0359 ns | 0.0622 ns | 0.32 | 0.00 | - * 'Copy using Marshal.Copy' | 100 | 34.3820 ns | 1.1868 ns | 2.0555 ns | 1.09 | 0.05 | - * - * 'Copy using Array.Copy()' | 1000 | 40.9743 ns | 0.0521 ns | 0.0902 ns | 1.00 | 0.00 | - * 'Copy using Unsafe' | 1000 | 42.7840 ns | 2.0139 ns | 3.4882 ns | 1.04 | 0.07 | - * 'Copy using Buffer.BlockCopy()' | 1000 | 33.7361 ns | 0.0751 ns | 0.1300 ns | 0.82 | 0.00 | - * 'Copy using Buffer.MemoryCopy' | 1000 | 35.7541 ns | 0.0480 ns | 0.0832 ns | 0.87 | 0.00 | - * 'Copy using Marshal.Copy' | 1000 | 42.2028 ns | 0.2769 ns | 0.4795 ns | 1.03 | 0.01 | - * - * 'Copy using Array.Copy()' | 10000 | 200.0438 ns | 0.2251 ns | 0.3899 ns | 1.00 | 0.00 | - * 'Copy using Unsafe' | 10000 | 389.6957 ns | 13.2770 ns | 22.9964 ns | 1.95 | 0.09 | - * 'Copy using Buffer.BlockCopy()' | 10000 | 191.3478 ns | 0.1557 ns | 0.2697 ns | 0.96 | 0.00 | - * 'Copy using Buffer.MemoryCopy' | 10000 | 196.4679 ns | 0.2731 ns | 0.4730 ns | 0.98 | 0.00 | - * 'Copy using Marshal.Copy' | 10000 | 202.5392 ns | 0.5561 ns | 0.9631 ns | 1.01 | 0.00 | - * - */ - } -} diff --git a/tests/ImageSharp.Benchmarks/General/CopyBuffers.cs b/tests/ImageSharp.Benchmarks/General/CopyBuffers.cs new file mode 100644 index 0000000000..117cdba418 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/CopyBuffers.cs @@ -0,0 +1,184 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +using System; +using System.Buffers; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using BenchmarkDotNet.Attributes; + +namespace SixLabors.ImageSharp.Benchmarks.General +{ + /// + /// Compare different methods for copying native and/or managed buffers. + /// Conclusions: + /// - Span.CopyTo() has terrible performance on classic .NET Framework + /// - Buffer.MemoryCopy() performance is good enough for all sizes (but needs pinning) + /// + [Config(typeof(Config.ShortClr))] + public class CopyBuffers + { + private byte[] destArray; + + private MemoryHandle destHandle; + + private Memory destMemory; + + private byte[] sourceArray; + + private MemoryHandle sourceHandle; + + private Memory sourceMemory; + + [Params(10, 50, 100, 1000, 10000)] + public int Count { get; set; } + + + [GlobalSetup] + public void Setup() + { + this.sourceArray = new byte[this.Count]; + this.sourceMemory = new Memory(this.sourceArray); + this.sourceHandle = this.sourceMemory.Pin(); + + this.destArray = new byte[this.Count]; + this.destMemory = new Memory(this.destArray); + this.destHandle = this.destMemory.Pin(); + } + + [GlobalCleanup] + public void Cleanup() + { + this.sourceHandle.Dispose(); + this.destHandle.Dispose(); + } + + [Benchmark(Baseline = true, Description = "Array.Copy()")] + public void ArrayCopy() + { + Array.Copy(this.sourceArray, this.destArray, this.Count); + } + + [Benchmark(Description = "Buffer.BlockCopy()")] + public void BufferBlockCopy() + { + Buffer.BlockCopy(this.sourceArray, 0, this.destArray, 0, this.Count); + } + + [Benchmark(Description = "Buffer.MemoryCopy()")] + public unsafe void BufferMemoryCopy() + { + void* pinnedDestination = this.destHandle.Pointer; + void* pinnedSource = this.sourceHandle.Pointer; + Buffer.MemoryCopy(pinnedSource, pinnedDestination, this.Count, this.Count); + } + + + [Benchmark(Description = "Marshal.Copy()")] + public unsafe void MarshalCopy() + { + void* pinnedDestination = this.destHandle.Pointer; + Marshal.Copy(this.sourceArray, 0, (IntPtr)pinnedDestination, this.Count); + } + + [Benchmark(Description = "Span.CopyTo()")] + public void SpanCopyTo() + { + this.sourceMemory.Span.CopyTo(this.destMemory.Span); + } + + [Benchmark(Description = "Unsafe.CopyBlock()")] + public unsafe void UnsafeCopyBlock() + { + void* pinnedDestination = this.destHandle.Pointer; + void* pinnedSource = this.sourceHandle.Pointer; + Unsafe.CopyBlock(pinnedSource, pinnedDestination, (uint)this.Count); + } + + // BenchmarkDotNet=v0.11.3, OS=Windows 10.0.17134.706 (1803/April2018Update/Redstone4) + // Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores + // Frequency=2742189 Hz, Resolution=364.6722 ns, Timer=TSC + // .NET Core SDK=2.2.202 + // [Host] : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT + // Clr : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3394.0 + // Core : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT + // + // IterationCount=3 LaunchCount=1 WarmupCount=3 + // + // Method | Job | Runtime | Count | Mean | Error | StdDev | Ratio | RatioSD | Gen 0/1k Op | Gen 1/1k Op | Gen 2/1k Op | Allocated Memory/Op | + // -------------------- |----- |-------- |------ |-----------:|-----------:|----------:|------:|--------:|------------:|------------:|------------:|--------------------:| + // Array.Copy() | Clr | Clr | 10 | 23.579 ns | 1.6836 ns | 0.0923 ns | 1.00 | 0.00 | - | - | - | - | + // Buffer.BlockCopy() | Clr | Clr | 10 | 11.796 ns | 0.5280 ns | 0.0289 ns | 0.50 | 0.00 | - | - | - | - | + // Buffer.MemoryCopy() | Clr | Clr | 10 | 3.206 ns | 8.1741 ns | 0.4480 ns | 0.14 | 0.02 | - | - | - | - | + // Marshal.Copy() | Clr | Clr | 10 | 15.577 ns | 2.0937 ns | 0.1148 ns | 0.66 | 0.00 | - | - | - | - | + // Span.CopyTo() | Clr | Clr | 10 | 32.287 ns | 2.4107 ns | 0.1321 ns | 1.37 | 0.01 | - | - | - | - | + // Unsafe.CopyBlock() | Clr | Clr | 10 | 3.266 ns | 0.3848 ns | 0.0211 ns | 0.14 | 0.00 | - | - | - | - | + // | | | | | | | | | | | | | + // Array.Copy() | Core | Core | 10 | 19.713 ns | 7.3026 ns | 0.4003 ns | 1.00 | 0.00 | - | - | - | - | + // Buffer.BlockCopy() | Core | Core | 10 | 7.332 ns | 0.5465 ns | 0.0300 ns | 0.37 | 0.01 | - | - | - | - | + // Buffer.MemoryCopy() | Core | Core | 10 | 2.476 ns | 0.3476 ns | 0.0191 ns | 0.13 | 0.00 | - | - | - | - | + // Marshal.Copy() | Core | Core | 10 | 15.575 ns | 0.1335 ns | 0.0073 ns | 0.79 | 0.02 | - | - | - | - | + // Span.CopyTo() | Core | Core | 10 | 25.321 ns | 2.3556 ns | 0.1291 ns | 1.28 | 0.02 | - | - | - | - | + // Unsafe.CopyBlock() | Core | Core | 10 | 2.204 ns | 0.1836 ns | 0.0101 ns | 0.11 | 0.00 | - | - | - | - | + // | | | | | | | | | | | | | + // Array.Copy() | Clr | Clr | 50 | 35.217 ns | 2.7642 ns | 0.1515 ns | 1.00 | 0.00 | - | - | - | - | + // Buffer.BlockCopy() | Clr | Clr | 50 | 24.224 ns | 0.8737 ns | 0.0479 ns | 0.69 | 0.00 | - | - | - | - | + // Buffer.MemoryCopy() | Clr | Clr | 50 | 3.827 ns | 4.8733 ns | 0.2671 ns | 0.11 | 0.01 | - | - | - | - | + // Marshal.Copy() | Clr | Clr | 50 | 28.103 ns | 1.3570 ns | 0.0744 ns | 0.80 | 0.00 | - | - | - | - | + // Span.CopyTo() | Clr | Clr | 50 | 34.137 ns | 2.9274 ns | 0.1605 ns | 0.97 | 0.01 | - | - | - | - | + // Unsafe.CopyBlock() | Clr | Clr | 50 | 4.999 ns | 0.1778 ns | 0.0097 ns | 0.14 | 0.00 | - | - | - | - | + // | | | | | | | | | | | | | + // Array.Copy() | Core | Core | 50 | 20.925 ns | 1.0219 ns | 0.0560 ns | 1.00 | 0.00 | - | - | - | - | + // Buffer.BlockCopy() | Core | Core | 50 | 8.083 ns | 0.2158 ns | 0.0118 ns | 0.39 | 0.00 | - | - | - | - | + // Buffer.MemoryCopy() | Core | Core | 50 | 2.919 ns | 0.2878 ns | 0.0158 ns | 0.14 | 0.00 | - | - | - | - | + // Marshal.Copy() | Core | Core | 50 | 16.663 ns | 0.2505 ns | 0.0137 ns | 0.80 | 0.00 | - | - | - | - | + // Span.CopyTo() | Core | Core | 50 | 26.940 ns | 11.5855 ns | 0.6350 ns | 1.29 | 0.03 | - | - | - | - | + // Unsafe.CopyBlock() | Core | Core | 50 | 1.940 ns | 0.6327 ns | 0.0347 ns | 0.09 | 0.00 | - | - | - | - | + // | | | | | | | | | | | | | + // Array.Copy() | Clr | Clr | 100 | 39.284 ns | 0.5647 ns | 0.0310 ns | 1.00 | 0.00 | - | - | - | - | + // Buffer.BlockCopy() | Clr | Clr | 100 | 28.930 ns | 0.6774 ns | 0.0371 ns | 0.74 | 0.00 | - | - | - | - | + // Buffer.MemoryCopy() | Clr | Clr | 100 | 5.859 ns | 2.7931 ns | 0.1531 ns | 0.15 | 0.00 | - | - | - | - | + // Marshal.Copy() | Clr | Clr | 100 | 36.529 ns | 0.9886 ns | 0.0542 ns | 0.93 | 0.00 | - | - | - | - | + // Span.CopyTo() | Clr | Clr | 100 | 36.152 ns | 1.5109 ns | 0.0828 ns | 0.92 | 0.00 | - | - | - | - | + // Unsafe.CopyBlock() | Clr | Clr | 100 | 9.317 ns | 0.4342 ns | 0.0238 ns | 0.24 | 0.00 | - | - | - | - | + // | | | | | | | | | | | | | + // Array.Copy() | Core | Core | 100 | 22.899 ns | 8.4066 ns | 0.4608 ns | 1.00 | 0.00 | - | - | - | - | + // Buffer.BlockCopy() | Core | Core | 100 | 10.696 ns | 0.8106 ns | 0.0444 ns | 0.47 | 0.01 | - | - | - | - | + // Buffer.MemoryCopy() | Core | Core | 100 | 4.102 ns | 0.9040 ns | 0.0496 ns | 0.18 | 0.01 | - | - | - | - | + // Marshal.Copy() | Core | Core | 100 | 17.917 ns | 2.6490 ns | 0.1452 ns | 0.78 | 0.01 | - | - | - | - | + // Span.CopyTo() | Core | Core | 100 | 28.247 ns | 0.6375 ns | 0.0349 ns | 1.23 | 0.03 | - | - | - | - | + // Unsafe.CopyBlock() | Core | Core | 100 | 3.611 ns | 0.4792 ns | 0.0263 ns | 0.16 | 0.00 | - | - | - | - | + // | | | | | | | | | | | | | + // Array.Copy() | Clr | Clr | 1000 | 48.907 ns | 4.4228 ns | 0.2424 ns | 1.00 | 0.00 | - | - | - | - | + // Buffer.BlockCopy() | Clr | Clr | 1000 | 40.653 ns | 1.4055 ns | 0.0770 ns | 0.83 | 0.01 | - | - | - | - | + // Buffer.MemoryCopy() | Clr | Clr | 1000 | 24.720 ns | 1.2651 ns | 0.0693 ns | 0.51 | 0.00 | - | - | - | - | + // Marshal.Copy() | Clr | Clr | 1000 | 42.336 ns | 2.2466 ns | 0.1231 ns | 0.87 | 0.00 | - | - | - | - | + // Span.CopyTo() | Clr | Clr | 1000 | 70.735 ns | 2.6215 ns | 0.1437 ns | 1.45 | 0.01 | - | - | - | - | + // Unsafe.CopyBlock() | Clr | Clr | 1000 | 44.520 ns | 0.9641 ns | 0.0528 ns | 0.91 | 0.00 | - | - | - | - | + // | | | | | | | | | | | | | + // Array.Copy() | Core | Core | 1000 | 46.286 ns | 11.6373 ns | 0.6379 ns | 1.00 | 0.00 | - | - | - | - | + // Buffer.BlockCopy() | Core | Core | 1000 | 34.243 ns | 7.2264 ns | 0.3961 ns | 0.74 | 0.01 | - | - | - | - | + // Buffer.MemoryCopy() | Core | Core | 1000 | 23.135 ns | 0.3153 ns | 0.0173 ns | 0.50 | 0.01 | - | - | - | - | + // Marshal.Copy() | Core | Core | 1000 | 46.219 ns | 1.2869 ns | 0.0705 ns | 1.00 | 0.01 | - | - | - | - | + // Span.CopyTo() | Core | Core | 1000 | 45.371 ns | 3.3581 ns | 0.1841 ns | 0.98 | 0.02 | - | - | - | - | + // Unsafe.CopyBlock() | Core | Core | 1000 | 29.347 ns | 1.1349 ns | 0.0622 ns | 0.63 | 0.01 | - | - | - | - | + // | | | | | | | | | | | | | + // Array.Copy() | Clr | Clr | 10000 | 218.445 ns | 9.2567 ns | 0.5074 ns | 1.00 | 0.00 | - | - | - | - | + // Buffer.BlockCopy() | Clr | Clr | 10000 | 209.610 ns | 6.7447 ns | 0.3697 ns | 0.96 | 0.00 | - | - | - | - | + // Buffer.MemoryCopy() | Clr | Clr | 10000 | 213.061 ns | 66.6490 ns | 3.6533 ns | 0.98 | 0.02 | - | - | - | - | + // Marshal.Copy() | Clr | Clr | 10000 | 214.426 ns | 27.7722 ns | 1.5223 ns | 0.98 | 0.00 | - | - | - | - | + // Span.CopyTo() | Clr | Clr | 10000 | 486.728 ns | 12.1537 ns | 0.6662 ns | 2.23 | 0.00 | - | - | - | - | + // Unsafe.CopyBlock() | Clr | Clr | 10000 | 452.973 ns | 25.1490 ns | 1.3785 ns | 2.07 | 0.00 | - | - | - | - | + // | | | | | | | | | | | | | + // Array.Copy() | Core | Core | 10000 | 203.365 ns | 3.2200 ns | 0.1765 ns | 1.00 | 0.00 | - | - | - | - | + // Buffer.BlockCopy() | Core | Core | 10000 | 193.319 ns | 8.3370 ns | 0.4570 ns | 0.95 | 0.00 | - | - | - | - | + // Buffer.MemoryCopy() | Core | Core | 10000 | 196.541 ns | 37.8056 ns | 2.0723 ns | 0.97 | 0.01 | - | - | - | - | + // Marshal.Copy() | Core | Core | 10000 | 206.454 ns | 3.7652 ns | 0.2064 ns | 1.02 | 0.00 | - | - | - | - | + // Span.CopyTo() | Core | Core | 10000 | 214.799 ns | 3.0667 ns | 0.1681 ns | 1.06 | 0.00 | - | - | - | - | + // Unsafe.CopyBlock() | Core | Core | 10000 | 134.428 ns | 2.6024 ns | 0.1426 ns | 0.66 | 0.00 | - | - | - | - | + + } +} \ No newline at end of file From 398736a09bc4764415a4f52bc3f3b6ba5f1867ee Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 19 Apr 2019 16:47:01 +0200 Subject: [PATCH 071/223] moar RowInterval stuff --- src/ImageSharp/Memory/RowInterval.cs | 37 +++++++++++++- .../Transforms/Resize/ResizeWorker.cs | 43 ++++++++-------- .../Helpers/RowIntervalTests.cs | 49 +++++++++++++++++++ 3 files changed, 106 insertions(+), 23 deletions(-) diff --git a/src/ImageSharp/Memory/RowInterval.cs b/src/ImageSharp/Memory/RowInterval.cs index 835e880e94..7e144e37c5 100644 --- a/src/ImageSharp/Memory/RowInterval.cs +++ b/src/ImageSharp/Memory/RowInterval.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; + using SixLabors.Primitives; namespace SixLabors.ImageSharp.Memory @@ -8,7 +10,7 @@ namespace SixLabors.ImageSharp.Memory /// /// Represents an interval of rows in a and/or /// - internal readonly struct RowInterval + internal readonly struct RowInterval : IEquatable { /// /// Initializes a new instance of the struct. @@ -36,7 +38,40 @@ namespace SixLabors.ImageSharp.Memory /// public int Height => this.Max - this.Min; + + public static bool operator ==(RowInterval left, RowInterval right) + { + return left.Equals(right); + } + + public static bool operator !=(RowInterval left, RowInterval right) + { + return !left.Equals(right); + } + /// public override string ToString() => $"RowInterval [{this.Min}->{this.Max}]"; + + public RowInterval Slice(int start) => new RowInterval(this.Min + start, this.Max); + + public RowInterval Slice(int start, int length) => new RowInterval(this.Min + start, this.Min + start + length); + + public bool Equals(RowInterval other) + { + return this.Min == other.Min && this.Max == other.Max; + } + + public override bool Equals(object obj) + { + return !ReferenceEquals(null, obj) && obj is RowInterval other && this.Equals(other); + } + + public override int GetHashCode() + { + unchecked + { + return (this.Min * 397) ^ this.Max; + } + } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs index e9e39ef13b..66a17e2d76 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs @@ -45,6 +45,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms private readonly int windowHeight; + private RowInterval currentWindow; + public ResizeWorker( Configuration configuration, BufferArea source, @@ -82,14 +84,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms this.tempRowBuffer = configuration.MemoryAllocator.Allocate(this.sourceRectangle.Width); this.tempColumnBuffer = configuration.MemoryAllocator.Allocate(destWidth); - this.CurrentMinY = 0; - this.CurrentMaxY = this.windowHeight; + this.currentWindow = new RowInterval(0, this.windowHeight); } - public int CurrentMaxY { get; private set; } - - public int CurrentMinY { get; private set; } - public void Dispose() { this.buffer.Dispose(); @@ -100,18 +97,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms [MethodImpl(MethodImplOptions.AggressiveInlining)] public Span GetColumnSpan(int x, int startY) { - return this.buffer.GetRowSpan(x).Slice(startY - this.CurrentMinY); + return this.buffer.GetRowSpan(x).Slice(startY - this.currentWindow.Min); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Span GetColumnSpan(int x) - { - return this.buffer.GetRowSpan(x); - } public void Initialize() { - this.CalculateFirstPassValues(0, this.windowHeight); + this.CalculateFirstPassValues(this.currentWindow); } public void FillDestinationPixels(int minY, int maxY, int startY, Buffer2D destination) @@ -123,14 +115,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms // Ensure offsets are normalized for cropping and padding. ResizeKernel kernel = this.verticalKernelMap.GetKernel(y - startY); - while (kernel.StartIndex + kernel.Length > this.CurrentMaxY) + while (kernel.StartIndex + kernel.Length > this.currentWindow.Max) { this.Slide(); } ref Vector4 tempRowBase = ref MemoryMarshal.GetReference(tempColSpan); - int top = kernel.StartIndex - this.CurrentMinY; + int top = kernel.StartIndex - this.currentWindow.Min; for (int x = 0; x < this.destWidth; x++) { @@ -146,17 +138,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } } - public void Slide() + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Span GetColumnSpan(int x) + { + return this.buffer.GetRowSpan(x); + } + + private void Slide() { - this.CurrentMinY = this.CurrentMinY + this.windowBandDiameter; - this.CurrentMaxY = Math.Min(this.CurrentMaxY + this.windowBandDiameter, this.sourceRectangle.Height); - this.CalculateFirstPassValues(this.CurrentMinY, this.CurrentMaxY); + int minY = this.currentWindow.Min + this.windowBandDiameter; + int maxY = Math.Min(this.currentWindow.Max + this.windowBandDiameter, this.sourceRectangle.Height); + this.currentWindow = new RowInterval(minY, maxY); + this.CalculateFirstPassValues(this.currentWindow); } - private void CalculateFirstPassValues(int minY, int maxY) + private void CalculateFirstPassValues(RowInterval window) { Span tempRowSpan = this.tempRowBuffer.GetSpan(); - for (int y = minY; y < maxY; y++) + for (int y = window.Min; y < window.Max; y++) { Span sourceRow = this.source.GetRowSpan(y); @@ -167,7 +166,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms this.conversionModifiers); // ref Vector4 firstPassBaseRef = ref this.buffer.Span[y - top]; - Span firstPassSpan = this.buffer.Span.Slice(y - minY); + Span firstPassSpan = this.buffer.Span.Slice(y - window.Min); for (int x = this.destWorkingRect.Left; x < this.destWorkingRect.Right; x++) { diff --git a/tests/ImageSharp.Tests/Helpers/RowIntervalTests.cs b/tests/ImageSharp.Tests/Helpers/RowIntervalTests.cs index 629b3cdeb3..3aead6aaa9 100644 --- a/tests/ImageSharp.Tests/Helpers/RowIntervalTests.cs +++ b/tests/ImageSharp.Tests/Helpers/RowIntervalTests.cs @@ -34,5 +34,54 @@ namespace SixLabors.ImageSharp.Tests.Helpers Assert.True(Unsafe.AreSame(ref expected0, ref actual0)); } } + + [Fact] + public void Slice1() + { + RowInterval rowInterval = new RowInterval(10, 20); + RowInterval sliced = rowInterval.Slice(5); + + Assert.Equal(15, sliced.Min); + Assert.Equal(20, sliced.Max); + } + + [Fact] + public void Slice2() + { + RowInterval rowInterval = new RowInterval(10, 20); + RowInterval sliced = rowInterval.Slice(3, 5); + + Assert.Equal(13, sliced.Min); + Assert.Equal(18, sliced.Max); + } + + [Fact] + public void Equality_WhenTrue() + { + RowInterval a = new RowInterval(42, 123); + RowInterval b = new RowInterval(42, 123); + + Assert.True(a.Equals(b)); + Assert.True(a.Equals((object)b)); + Assert.True(a == b); + Assert.Equal(a.GetHashCode(), b.GetHashCode()); + } + + [Fact] + public void Equality_WhenFalse() + { + RowInterval a = new RowInterval(42, 123); + RowInterval b = new RowInterval(42, 125); + RowInterval c = new RowInterval(40, 123); + + Assert.False(a.Equals(b)); + Assert.False(c.Equals(a)); + Assert.False(b.Equals(c)); + + Assert.False(a.Equals((object)b)); + Assert.False(a.Equals(null)); + Assert.False(a == b); + Assert.True(a != c); + } } } From ae22e017c55a27b64c2380660a7a456a6da9bac1 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 19 Apr 2019 17:27:01 +0200 Subject: [PATCH 072/223] buffer.CopyColumns(...) --- src/ImageSharp/Memory/Buffer2DExtensions.cs | 144 +++++++++++------- .../ImageSharp.Tests/Memory/Buffer2DTests.cs | 28 ++++ .../TestUtilities/TestDataGenerator.cs | 13 +- 3 files changed, 130 insertions(+), 55 deletions(-) diff --git a/src/ImageSharp/Memory/Buffer2DExtensions.cs b/src/ImageSharp/Memory/Buffer2DExtensions.cs index 17ab6e2522..c8bbf01c7f 100644 --- a/src/ImageSharp/Memory/Buffer2DExtensions.cs +++ b/src/ImageSharp/Memory/Buffer2DExtensions.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.Runtime.CompilerServices; using SixLabors.Primitives; @@ -14,41 +15,90 @@ namespace SixLabors.ImageSharp.Memory internal static class Buffer2DExtensions { /// - /// Gets a to the backing buffer of . + /// Copy columns of inplace, + /// from positions starting at to positions at . /// - internal static Span GetSpan(this Buffer2D buffer) + public static unsafe void CopyColumns( + this Buffer2D buffer, + int sourceIndex, + int destIndex, + int columnCount) where T : struct { - return buffer.MemorySource.GetSpan(); + DebugGuard.NotNull(buffer, nameof(buffer)); + DebugGuard.MustBeGreaterThanOrEqualTo(sourceIndex, 0, nameof(sourceIndex)); + DebugGuard.MustBeGreaterThanOrEqualTo(destIndex, sourceIndex + columnCount, nameof(destIndex)); + DebugGuard.MustBeLessThanOrEqualTo(destIndex, buffer.Width - columnCount, nameof(destIndex)); + + int elementSize = Unsafe.SizeOf(); + int width = buffer.Width * elementSize; + int sOffset = sourceIndex * elementSize; + int dOffset = destIndex * elementSize; + long count = columnCount * elementSize; + + using (MemoryHandle handle = buffer.Memory.Pin()) + { + byte* basePtr = (byte*)handle.Pointer; + for (int y = 0; y < buffer.Height; y++) + { + byte* sPtr = basePtr + sOffset; + byte* dPtr = basePtr + dOffset; + + Buffer.MemoryCopy(sPtr, dPtr, count, count); + + basePtr += width; + } + } } /// - /// Gets a to the row 'y' beginning from the pixel at 'x'. + /// Returns a representing the full area of the buffer. /// - /// The buffer - /// The x coordinate (position in the row) - /// The y (row) coordinate /// The element type - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span GetRowSpan(this Buffer2D buffer, int x, int y) + /// The + /// The + public static Rectangle FullRectangle(this Buffer2D buffer) where T : struct { - return buffer.GetSpan().Slice((y * buffer.Width) + x, buffer.Width - x); + return new Rectangle(0, 0, buffer.Width, buffer.Height); } /// - /// Gets a to the row 'y' beginning from the pixel at the first pixel on that row. + /// Return a to the subarea represented by 'rectangle' /// - /// The buffer - /// The y (row) coordinate /// The element type - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span GetRowSpan(this Buffer2D buffer, int y) + /// The + /// The rectangle subarea + /// The + public static BufferArea GetArea(this Buffer2D buffer, in Rectangle rectangle) + where T : struct => + new BufferArea(buffer, rectangle); + + public static BufferArea GetArea(this Buffer2D buffer, int x, int y, int width, int height) + where T : struct => + new BufferArea(buffer, new Rectangle(x, y, width, height)); + + /// + /// Return a to the whole area of 'buffer' + /// + /// The element type + /// The + /// The + public static BufferArea GetArea(this Buffer2D buffer) + where T : struct => + new BufferArea(buffer); + + public static BufferArea GetAreaBetweenRows(this Buffer2D buffer, int minY, int maxY) + where T : struct => + new BufferArea(buffer, new Rectangle(0, minY, buffer.Width, maxY - minY)); + + /// + /// Gets a span for all the pixels in defined by + /// + public static Span GetMultiRowSpan(this Buffer2D buffer, in RowInterval rows) where T : struct { - return buffer.GetSpan().Slice(y * buffer.Width, buffer.Width); + return buffer.Span.Slice(rows.Min * buffer.Width, rows.Height * buffer.Width); } /// @@ -66,61 +116,53 @@ namespace SixLabors.ImageSharp.Memory } /// - /// Returns the size of the buffer. + /// Gets a to the row 'y' beginning from the pixel at 'x'. /// + /// The buffer + /// The x coordinate (position in the row) + /// The y (row) coordinate /// The element type - /// The - /// The of the buffer - public static Size Size(this Buffer2D buffer) + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span GetRowSpan(this Buffer2D buffer, int x, int y) where T : struct { - return new Size(buffer.Width, buffer.Height); + return buffer.GetSpan().Slice((y * buffer.Width) + x, buffer.Width - x); } /// - /// Returns a representing the full area of the buffer. + /// Gets a to the row 'y' beginning from the pixel at the first pixel on that row. /// + /// The buffer + /// The y (row) coordinate /// The element type - /// The - /// The - public static Rectangle FullRectangle(this Buffer2D buffer) + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span GetRowSpan(this Buffer2D buffer, int y) where T : struct { - return new Rectangle(0, 0, buffer.Width, buffer.Height); + return buffer.GetSpan().Slice(y * buffer.Width, buffer.Width); } /// - /// Return a to the subarea represented by 'rectangle' - /// - /// The element type - /// The - /// The rectangle subarea - /// The - public static BufferArea GetArea(this Buffer2D buffer, in Rectangle rectangle) - where T : struct => new BufferArea(buffer, rectangle); - - public static BufferArea GetArea(this Buffer2D buffer, int x, int y, int width, int height) - where T : struct => new BufferArea(buffer, new Rectangle(x, y, width, height)); - - public static BufferArea GetAreaBetweenRows(this Buffer2D buffer, int minY, int maxY) - where T : struct => new BufferArea(buffer, new Rectangle(0, minY, buffer.Width, maxY - minY)); - - /// - /// Return a to the whole area of 'buffer' + /// Returns the size of the buffer. /// /// The element type /// The - /// The - public static BufferArea GetArea(this Buffer2D buffer) - where T : struct => new BufferArea(buffer); + /// The of the buffer + public static Size Size(this Buffer2D buffer) + where T : struct + { + return new Size(buffer.Width, buffer.Height); + } /// - /// Gets a span for all the pixels in defined by + /// Gets a to the backing buffer of . /// - public static Span GetMultiRowSpan(this Buffer2D buffer, in RowInterval rows) + internal static Span GetSpan(this Buffer2D buffer) where T : struct { - return buffer.Span.Slice(rows.Min * buffer.Width, rows.Height * buffer.Width); + return buffer.MemorySource.GetSpan(); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs index 19ec725f27..3d40875461 100644 --- a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs +++ b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs @@ -127,5 +127,33 @@ namespace SixLabors.ImageSharp.Tests.Memory Assert.Equal(new Size(10, 5), b.Size()); } } + + [Theory] + [InlineData(100, 20, 0, 90, 10)] + [InlineData(100, 3, 0, 50, 50)] + [InlineData(123, 23, 10, 80, 13)] + [InlineData(10, 1, 3, 6, 3)] + [InlineData(2, 2, 0, 1, 1)] + [InlineData(5, 1, 1, 3, 2)] + public void CopyColumns(int width, int height, int startIndex, int destIndex, int columnCount) + { + Random rnd = new Random(123); + using (Buffer2D b = this.MemoryAllocator.Allocate2D(width, height)) + { + rnd.RandomFill(b.Span, 0, 1); + + b.CopyColumns(startIndex, destIndex, columnCount); + + for (int y = 0; y < b.Height; y++) + { + Span row = b.GetRowSpan(y); + + Span s = row.Slice(startIndex, columnCount); + Span d = row.Slice(destIndex, columnCount); + + Xunit.Assert.True(s.SequenceEqual(d)); + } + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/TestDataGenerator.cs b/tests/ImageSharp.Tests/TestUtilities/TestDataGenerator.cs index e3d8bf3806..4ccb387451 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestDataGenerator.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestDataGenerator.cs @@ -23,14 +23,19 @@ namespace SixLabors.ImageSharp.Tests { float[] values = new float[length]; - for (int i = 0; i < length; i++) - { - values[i] = GetRandomFloat(rnd, minVal, maxVal); - } + RandomFill(rnd, values, minVal, maxVal); return values; } + public static void RandomFill(this Random rnd, Span destination, float minVal, float maxVal) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = GetRandomFloat(rnd, minVal, maxVal); + } + } + /// /// Creates an of the given length consisting of random values between the two ranges. /// From c86551021a6b869f50e90a76b3a5ca2d9b858c4b Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 19 Apr 2019 17:47:35 +0200 Subject: [PATCH 073/223] simplify ResizeWorker logic --- src/ImageSharp/Memory/RowInterval.cs | 1 - .../Transforms/Resize/ResizeProcessor.cs | 18 +++++---- .../Transforms/Resize/ResizeWorker.cs | 37 +++++++++---------- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/ImageSharp/Memory/RowInterval.cs b/src/ImageSharp/Memory/RowInterval.cs index 7e144e37c5..3ee7ae774d 100644 --- a/src/ImageSharp/Memory/RowInterval.cs +++ b/src/ImageSharp/Memory/RowInterval.cs @@ -38,7 +38,6 @@ namespace SixLabors.ImageSharp.Memory /// public int Height => this.Max - this.Min; - public static bool operator ==(RowInterval left, RowInterval right) { return left.Equals(right); diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs index 63dd5efca0..e75f6014ab 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs @@ -197,7 +197,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms int startY = this.TargetRectangle.Y; int startX = this.TargetRectangle.X; - var destWorkingRect = Rectangle.Intersect( + var targetWorkingRect = Rectangle.Intersect( this.TargetRectangle, new Rectangle(0, 0, width, height)); @@ -208,7 +208,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms float heightFactor = sourceRectangle.Height / (float)this.TargetRectangle.Height; ParallelHelper.IterateRows( - destWorkingRect, + targetWorkingRect, configuration, rows => { @@ -219,7 +219,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms source.GetPixelRowSpan((int)(((y - startY) * heightFactor) + sourceY)); Span targetRow = destination.GetPixelRowSpan(y); - for (int x = destWorkingRect.Left; x < destWorkingRect.Right; x++) + for (int x = targetWorkingRect.Left; x < targetWorkingRect.Right; x++) { // X coordinates of source points targetRow[x] = sourceRow[(int)(((x - startX) * widthFactor) + sourceX)]; @@ -237,8 +237,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms BufferArea sourceArea = source.PixelBuffer.GetArea(sourceRectangle); - // If we want to reintroduce processing: - // it's possible to launch multiple workers for different regions of the image + // To reintroduce parallel processing, we to launch multiple workers + // for different row intervals of the image. using (var worker = new ResizeWorker( configuration, sourceArea, @@ -246,11 +246,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms this.horizontalKernelMap, this.verticalKernelMap, width, - destWorkingRect, - startX)) + targetWorkingRect, + this.TargetRectangle.Location)) { worker.Initialize(); - worker.FillDestinationPixels(destWorkingRect.Top, destWorkingRect.Bottom, startY, destination.PixelBuffer); + + var workingInterval = new RowInterval(targetWorkingRect.Top, targetWorkingRect.Bottom); + worker.FillDestinationPixels(workingInterval, destination.PixelBuffer); } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs index 66a17e2d76..339e8a3b2c 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs @@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms internal class ResizeWorker : IDisposable where TPixel : struct, IPixel { - private readonly Buffer2D buffer; + private readonly Buffer2D transposedFirstPassBuffer; private readonly Configuration configuration; @@ -29,8 +29,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms private readonly Rectangle sourceRectangle; - private readonly int startX; - private readonly IMemoryOwner tempRowBuffer; private readonly IMemoryOwner tempColumnBuffer; @@ -39,7 +37,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms private readonly int destWidth; - private readonly Rectangle destWorkingRect; + private readonly Rectangle targetWorkingRect; + + private readonly Point targetOrigin; private readonly int windowBandDiameter; @@ -54,8 +54,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms ResizeKernelMap horizontalKernelMap, ResizeKernelMap verticalKernelMap, int destWidth, - Rectangle destWorkingRect, - int startX) + Rectangle targetWorkingRect, + Point targetOrigin) { this.configuration = configuration; this.source = source; @@ -64,8 +64,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms this.horizontalKernelMap = horizontalKernelMap; this.verticalKernelMap = verticalKernelMap; this.destWidth = destWidth; - this.destWorkingRect = destWorkingRect; - this.startX = startX; + this.targetWorkingRect = targetWorkingRect; + this.targetOrigin = targetOrigin; this.windowBandDiameter = verticalKernelMap.MaxDiameter; @@ -76,7 +76,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms this.windowHeight = Math.Min(this.sourceRectangle.Height, numberOfWindowBands * this.windowBandDiameter); - this.buffer = configuration.MemoryAllocator.Allocate2D( + this.transposedFirstPassBuffer = configuration.MemoryAllocator.Allocate2D( this.windowHeight, destWidth, AllocationOptions.Clean); @@ -89,7 +89,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public void Dispose() { - this.buffer.Dispose(); + this.transposedFirstPassBuffer.Dispose(); this.tempRowBuffer.Dispose(); this.tempColumnBuffer.Dispose(); } @@ -97,23 +97,22 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms [MethodImpl(MethodImplOptions.AggressiveInlining)] public Span GetColumnSpan(int x, int startY) { - return this.buffer.GetRowSpan(x).Slice(startY - this.currentWindow.Min); + return this.transposedFirstPassBuffer.GetRowSpan(x).Slice(startY - this.currentWindow.Min); } - public void Initialize() { this.CalculateFirstPassValues(this.currentWindow); } - public void FillDestinationPixels(int minY, int maxY, int startY, Buffer2D destination) + public void FillDestinationPixels(RowInterval rowInterval, Buffer2D destination) { Span tempColSpan = this.tempColumnBuffer.GetSpan(); - for (int y = minY; y < maxY; y++) + for (int y = rowInterval.Min; y < rowInterval.Max; y++) { // Ensure offsets are normalized for cropping and padding. - ResizeKernel kernel = this.verticalKernelMap.GetKernel(y - startY); + ResizeKernel kernel = this.verticalKernelMap.GetKernel(y - this.targetOrigin.Y); while (kernel.StartIndex + kernel.Length > this.currentWindow.Max) { @@ -141,7 +140,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms [MethodImpl(MethodImplOptions.AggressiveInlining)] private Span GetColumnSpan(int x) { - return this.buffer.GetRowSpan(x); + return this.transposedFirstPassBuffer.GetRowSpan(x); } private void Slide() @@ -166,11 +165,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms this.conversionModifiers); // ref Vector4 firstPassBaseRef = ref this.buffer.Span[y - top]; - Span firstPassSpan = this.buffer.Span.Slice(y - window.Min); + Span firstPassSpan = this.transposedFirstPassBuffer.Span.Slice(y - window.Min); - for (int x = this.destWorkingRect.Left; x < this.destWorkingRect.Right; x++) + for (int x = this.targetWorkingRect.Left; x < this.targetWorkingRect.Right; x++) { - ResizeKernel kernel = this.horizontalKernelMap.GetKernel(x - this.startX); + ResizeKernel kernel = this.horizontalKernelMap.GetKernel(x - this.targetOrigin.X); firstPassSpan[x * this.windowHeight] = kernel.Convolve(tempRowSpan); // Unsafe.Add(ref firstPassBaseRef, x * this.sourceRectangle.Height) = kernel.Convolve(tempRowSpan); From bd8c06ad12025f1dec87b92118405ba9544b73b1 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 19 Apr 2019 21:00:39 +0200 Subject: [PATCH 074/223] WorkingBufferSizeHintInBytes_IsAppliedCorrectly --- .../Transforms/Resize/ResizeHelper.cs | 4 +- .../Transforms/Resize/ResizeWorker.cs | 20 +- .../Processors/Transforms/ResamplerTests.cs | 69 +++ .../Transforms/ResizeHelperTests.cs | 35 ++ .../Processors/Transforms/ResizeTests.cs | 143 +++--- .../BasicTestPatternProvider.cs | 2 +- .../ImageProviders/BlankProvider.cs | 2 +- .../ImageProviders/FileProvider.cs | 2 +- .../ImageProviders/TestImageProvider.cs | 2 +- .../ImageProviders/TestPatternProvider.cs | 3 +- .../TestUtilities/TestMemoryAllocator.cs | 39 +- .../Tests/TestImageProviderTests.cs | 458 ++++++++++-------- tests/Images/External | 2 +- 13 files changed, 474 insertions(+), 307 deletions(-) create mode 100644 tests/ImageSharp.Tests/Processing/Processors/Transforms/ResamplerTests.cs create mode 100644 tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeHelperTests.cs diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs index 595a7e852e..956e6b84e7 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs @@ -16,12 +16,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms internal static class ResizeHelper { public static unsafe int CalculateResizeWorkerHeightInWindowBands( - int windowBandDiameter, + int windowBandHeight, int width, int sizeLimitHintInBytes) { int sizeLimitHint = sizeLimitHintInBytes / sizeof(Vector4); - int sizeOfOneWindow = windowBandDiameter * width; + int sizeOfOneWindow = windowBandHeight * width; return Math.Max(2, sizeLimitHint / sizeOfOneWindow); } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs index 339e8a3b2c..ce28a00ee7 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs @@ -41,9 +41,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms private readonly Point targetOrigin; - private readonly int windowBandDiameter; + private readonly int windowBandHeight; - private readonly int windowHeight; + private readonly int workerHeight; private RowInterval currentWindow; @@ -67,24 +67,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms this.targetWorkingRect = targetWorkingRect; this.targetOrigin = targetOrigin; - this.windowBandDiameter = verticalKernelMap.MaxDiameter; + this.windowBandHeight = verticalKernelMap.MaxDiameter; int numberOfWindowBands = ResizeHelper.CalculateResizeWorkerHeightInWindowBands( - this.windowBandDiameter, + this.windowBandHeight, destWidth, configuration.WorkingBufferSizeHintInBytes); - this.windowHeight = Math.Min(this.sourceRectangle.Height, numberOfWindowBands * this.windowBandDiameter); + this.workerHeight = Math.Min(this.sourceRectangle.Height, numberOfWindowBands * this.windowBandHeight); this.transposedFirstPassBuffer = configuration.MemoryAllocator.Allocate2D( - this.windowHeight, + this.workerHeight, destWidth, AllocationOptions.Clean); this.tempRowBuffer = configuration.MemoryAllocator.Allocate(this.sourceRectangle.Width); this.tempColumnBuffer = configuration.MemoryAllocator.Allocate(destWidth); - this.currentWindow = new RowInterval(0, this.windowHeight); + this.currentWindow = new RowInterval(0, this.workerHeight); } public void Dispose() @@ -145,8 +145,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms private void Slide() { - int minY = this.currentWindow.Min + this.windowBandDiameter; - int maxY = Math.Min(this.currentWindow.Max + this.windowBandDiameter, this.sourceRectangle.Height); + int minY = this.currentWindow.Min + this.windowBandHeight; + int maxY = Math.Min(this.currentWindow.Max + this.windowBandHeight, this.sourceRectangle.Height); this.currentWindow = new RowInterval(minY, maxY); this.CalculateFirstPassValues(this.currentWindow); } @@ -170,7 +170,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms for (int x = this.targetWorkingRect.Left; x < this.targetWorkingRect.Right; x++) { ResizeKernel kernel = this.horizontalKernelMap.GetKernel(x - this.targetOrigin.X); - firstPassSpan[x * this.windowHeight] = kernel.Convolve(tempRowSpan); + firstPassSpan[x * this.workerHeight] = kernel.Convolve(tempRowSpan); // Unsafe.Add(ref firstPassBaseRef, x * this.sourceRectangle.Height) = kernel.Convolve(tempRowSpan); } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResamplerTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResamplerTests.cs new file mode 100644 index 0000000000..b7b4597c79 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResamplerTests.cs @@ -0,0 +1,69 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Transforms; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms +{ + public class ResamplerTests + { + [Theory] + [InlineData(-2, 0)] + [InlineData(-1, 0)] + [InlineData(0, 1)] + [InlineData(1, 0)] + [InlineData(2, 0)] + public static void BicubicWindowOscillatesCorrectly(float x, float expected) + { + IResampler sampler = KnownResamplers.Bicubic; + float result = sampler.GetValue(x); + + Assert.Equal(result, expected); + } + + [Theory] + [InlineData(-2, 0)] + [InlineData(-1, 0)] + [InlineData(0, 1)] + [InlineData(1, 0)] + [InlineData(2, 0)] + public static void Lanczos3WindowOscillatesCorrectly(float x, float expected) + { + IResampler sampler = KnownResamplers.Lanczos3; + float result = sampler.GetValue(x); + + Assert.Equal(result, expected); + } + + [Theory] + [InlineData(-4, 0)] + [InlineData(-2, 0)] + [InlineData(0, 1)] + [InlineData(2, 0)] + [InlineData(4, 0)] + public static void Lanczos5WindowOscillatesCorrectly(float x, float expected) + { + IResampler sampler = KnownResamplers.Lanczos5; + float result = sampler.GetValue(x); + + Assert.Equal(result, expected); + } + + [Theory] + [InlineData(-2, 0)] + [InlineData(-1, 0)] + [InlineData(0, 1)] + [InlineData(1, 0)] + [InlineData(2, 0)] + public static void TriangleWindowOscillatesCorrectly(float x, float expected) + { + IResampler sampler = KnownResamplers.Triangle; + float result = sampler.GetValue(x); + + Assert.Equal(result, expected); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeHelperTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeHelperTests.cs new file mode 100644 index 0000000000..b0d8ef6531 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeHelperTests.cs @@ -0,0 +1,35 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Processing.Processors.Transforms; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms +{ + public class ResizeHelperTests + { + + [Theory] + [InlineData(20, 100, 1, 2)] + [InlineData(20, 100, 20*100*16, 2)] + [InlineData(20, 100, 40*100*16, 2)] + [InlineData(20, 100, 59*100*16, 2)] + [InlineData(20, 100, 60*100*16, 3)] + [InlineData(17, 63, 5*17*63*16, 5)] + [InlineData(17, 63, 5*17*63*16+1, 5)] + [InlineData(17, 63, 6*17*63*16-1, 5)] + [InlineData(33, 400, 1*1024*1024, 4)] + [InlineData(33, 400, 8*1024*1024, 39)] + [InlineData(50, 300, 1*1024*1024, 4)] + public void CalculateResizeWorkerHeightInWindowBands( + int windowDiameter, + int width, + int sizeLimitHintInBytes, + int expectedCount) + { + int actualCount = ResizeHelper.CalculateResizeWorkerHeightInWindowBands(windowDiameter, width, sizeLimitHintInBytes); + Assert.Equal(expectedCount, actualCount); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs index 5325581662..2ffb42916f 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs @@ -2,12 +2,17 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Linq; +using System.Numerics; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Transforms; +using SixLabors.ImageSharp.Tests.Memory; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; +using SixLabors.Memory; using SixLabors.Primitives; using Xunit; @@ -35,84 +40,6 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.07F); - [Theory] - [InlineData(20, 100, 1, 2)] - [InlineData(20, 100, 20*100*16, 2)] - [InlineData(20, 100, 40*100*16, 2)] - [InlineData(20, 100, 59*100*16, 2)] - [InlineData(20, 100, 60*100*16, 3)] - [InlineData(17, 63, 5*17*63*16, 5)] - [InlineData(17, 63, 5*17*63*16+1, 5)] - [InlineData(17, 63, 6*17*63*16-1, 5)] - [InlineData(33, 400, 1*1024*1024, 4)] - [InlineData(33, 400, 8*1024*1024, 39)] - [InlineData(50, 300, 1*1024*1024, 4)] - public void CalculateResizeWorkerHeightInWindowBands( - int windowDiameter, - int width, - int sizeLimitHintInBytes, - int expectedCount) - { - int actualCount = ResizeHelper.CalculateResizeWorkerHeightInWindowBands(windowDiameter, width, sizeLimitHintInBytes); - Assert.Equal(expectedCount, actualCount); - } - - [Theory] - [InlineData(-2, 0)] - [InlineData(-1, 0)] - [InlineData(0, 1)] - [InlineData(1, 0)] - [InlineData(2, 0)] - public static void BicubicWindowOscillatesCorrectly(float x, float expected) - { - IResampler sampler = KnownResamplers.Bicubic; - float result = sampler.GetValue(x); - - Assert.Equal(result, expected); - } - - [Theory] - [InlineData(-2, 0)] - [InlineData(-1, 0)] - [InlineData(0, 1)] - [InlineData(1, 0)] - [InlineData(2, 0)] - public static void Lanczos3WindowOscillatesCorrectly(float x, float expected) - { - IResampler sampler = KnownResamplers.Lanczos3; - float result = sampler.GetValue(x); - - Assert.Equal(result, expected); - } - - [Theory] - [InlineData(-4, 0)] - [InlineData(-2, 0)] - [InlineData(0, 1)] - [InlineData(2, 0)] - [InlineData(4, 0)] - public static void Lanczos5WindowOscillatesCorrectly(float x, float expected) - { - IResampler sampler = KnownResamplers.Lanczos5; - float result = sampler.GetValue(x); - - Assert.Equal(result, expected); - } - - [Theory] - [InlineData(-2, 0)] - [InlineData(-1, 0)] - [InlineData(0, 1)] - [InlineData(1, 0)] - [InlineData(2, 0)] - public static void TriangleWindowOscillatesCorrectly(float x, float expected) - { - IResampler sampler = KnownResamplers.Triangle; - float result = sampler.GetValue(x); - - Assert.Equal(result, expected); - } - [Theory] [WithBasicTestPatternImages(15, 12, PixelTypes.Rgba32, 2, 3, 1, 2)] [WithBasicTestPatternImages(2, 256, PixelTypes.Rgba32, 1, 1, 1, 8)] @@ -136,6 +63,66 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms } } + private static readonly int SizeOfVector4 = Unsafe.SizeOf(); + + [Theory] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32, 50)] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32, 60)] + [WithTestPatternImages(100, 400, PixelTypes.Rgba32, 110)] + [WithTestPatternImages(79, 97, PixelTypes.Rgba32, 73)] + [WithTestPatternImages(79, 97, PixelTypes.Rgba32, 5)] + [WithTestPatternImages(47, 193, PixelTypes.Rgba32, 73)] + [WithTestPatternImages(23, 211, PixelTypes.Rgba32, 31)] + public void WorkingBufferSizeHintInBytes_IsAppliedCorrectly( + TestImageProvider provider, + int workingBufferLimitInRows) + where TPixel : struct, IPixel + { + using (Image image0 = provider.GetImage()) + { + Size destSize = image0.Size() / 4; + + Configuration configuration = Configuration.CreateDefaultInstance(); + + int workingBufferSizeHintInBytes = workingBufferLimitInRows * destSize.Width * SizeOfVector4; + TestMemoryAllocator allocator = new TestMemoryAllocator(); + configuration.MemoryAllocator = allocator; + configuration.WorkingBufferSizeHintInBytes = workingBufferSizeHintInBytes; + + var verticalKernelMap = ResizeKernelMap.Calculate( + KnownResamplers.Bicubic, + destSize.Height, + image0.Height, + Configuration.Default.MemoryAllocator); + int minimumWorkerAllocationInBytes = verticalKernelMap.MaxDiameter * 2 * destSize.Width * SizeOfVector4; + verticalKernelMap.Dispose(); + + using (Image image = image0.Clone(configuration)) + { + image.Mutate(x => x.Resize(destSize, KnownResamplers.Bicubic, false)); + + image.DebugSave( + provider, + testOutputDetails: workingBufferLimitInRows, + appendPixelTypeToFileName: false); + image.CompareToReferenceOutput( + provider, + testOutputDetails: workingBufferLimitInRows, + appendPixelTypeToFileName: false); + + Assert.NotEmpty(allocator.AllocationLog); + + var internalAllocations = allocator.AllocationLog.Where( + e => e.ElementType == typeof(Vector4)).ToArray(); + + int maxAllocationSize = internalAllocations.Max(e => e.LengthInBytes); + + + Assert.True(maxAllocationSize <= Math.Max(workingBufferSizeHintInBytes, minimumWorkerAllocationInBytes)); + } + } + } + [Theory] [WithTestPatternImages(100, 100, DefaultPixelType)] public void Resize_Compand(TestImageProvider provider) diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BasicTestPatternProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BasicTestPatternProvider.cs index 47bb22aeb3..de203535c6 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BasicTestPatternProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BasicTestPatternProvider.cs @@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Tests public override Image GetImage() { - var result = new Image(this.Width, this.Height); + var result = new Image(this.Configuration, this.Width, this.Height); TPixel topLeftColor = NamedColors.Red; TPixel topRightColor = NamedColors.Green; diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs index 1275e522f8..dae2f0cfe4 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs @@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Tests protected int Width { get; private set; } - public override Image GetImage() => new Image(this.Width, this.Height); + public override Image GetImage() => new Image(this.Configuration, this.Width, this.Height); public override void Deserialize(IXunitSerializationInfo info) diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs index 3ed696c472..8c5b88b280 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs @@ -152,7 +152,7 @@ namespace SixLabors.ImageSharp.Tests Image cachedImage = cache.GetOrAdd(key, _ => this.LoadImage(decoder)); - return cachedImage.Clone(); + return cachedImage.Clone(this.Configuration); } public override void Deserialize(IXunitSerializationInfo info) diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs index 52f66a78b5..15fab9b2bf 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs @@ -106,7 +106,7 @@ namespace SixLabors.ImageSharp.Tests /// public Image GetImage(Action> operationsToApply) { - Image img = GetImage(); + Image img = this.GetImage(); img.Mutate(operationsToApply); return img; } diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs index 336cdbf11f..6df8c85016 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs @@ -45,9 +45,8 @@ namespace SixLabors.ImageSharp.Tests DrawTestPattern(image); TestImages.Add(this.SourceFileOrDescription, image); } + return TestImages[this.SourceFileOrDescription].Clone(this.Configuration); } - - return TestImages[this.SourceFileOrDescription].Clone(); } /// diff --git a/tests/ImageSharp.Tests/TestUtilities/TestMemoryAllocator.cs b/tests/ImageSharp.Tests/TestUtilities/TestMemoryAllocator.cs index dc755e6827..5613e7b684 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestMemoryAllocator.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestMemoryAllocator.cs @@ -1,5 +1,7 @@ using System; using System.Buffers; +using System.Collections.Generic; +using System.Numerics; using System.Runtime.InteropServices; using SixLabors.Memory; @@ -8,6 +10,8 @@ namespace SixLabors.ImageSharp.Tests.Memory { internal class TestMemoryAllocator : MemoryAllocator { + private List allocationLog = new List(); + public TestMemoryAllocator(byte dirtyValue = 42) { this.DirtyValue = dirtyValue; @@ -18,10 +22,11 @@ namespace SixLabors.ImageSharp.Tests.Memory /// public byte DirtyValue { get; } + public IList AllocationLog => this.allocationLog; + public override IMemoryOwner Allocate(int length, AllocationOptions options = AllocationOptions.None) - { + { T[] array = this.AllocateArray(length, options); - return new BasicArrayBuffer(array, length); } @@ -34,6 +39,7 @@ namespace SixLabors.ImageSharp.Tests.Memory private T[] AllocateArray(int length, AllocationOptions options) where T : struct { + this.allocationLog.Add(AllocationRequest.Create(options, length)); var array = new T[length + 42]; if (options == AllocationOptions.None) @@ -44,6 +50,35 @@ namespace SixLabors.ImageSharp.Tests.Memory return array; } + + public struct AllocationRequest + { + private AllocationRequest(Type elementType, AllocationOptions allocationOptions, int length, int lengthInBytes) + { + this.ElementType = elementType; + this.AllocationOptions = allocationOptions; + this.Length = length; + this.LengthInBytes = lengthInBytes; + + if (elementType == typeof(Vector4)) + { + + } + } + + public static AllocationRequest Create(AllocationOptions allocationOptions, int length) + { + Type type = typeof(T); + int elementSize = Marshal.SizeOf(type); + return new AllocationRequest(type, allocationOptions, length, length * elementSize); + } + + public Type ElementType { get; } + public AllocationOptions AllocationOptions { get; } + public int Length { get; } + public int LengthInBytes { get; } + } + /// /// Wraps an array as an instance. diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs index 1bee34f1a7..4ef6a582c9 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs @@ -4,112 +4,135 @@ using System; using System.Collections.Concurrent; using System.IO; + +using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; + using Xunit; using Xunit.Abstractions; + // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests { public class TestImageProviderTests { + public static readonly TheoryData BasicData = new TheoryData() + { + TestImageProvider.Blank(10, 20), + TestImageProvider.Blank(10, 20), + }; + + public static readonly TheoryData FileData = new TheoryData() + { + TestImageProvider.File(TestImages.Bmp.Car), + TestImageProvider.File( + TestImages.Bmp.F) + }; + + public static string[] AllBmpFiles = { TestImages.Bmp.F, TestImages.Bmp.Bit8 }; + public TestImageProviderTests(ITestOutputHelper output) => this.Output = output; private ITestOutputHelper Output { get; } - [Theory] - [WithBlankImages(1, 1, PixelTypes.Rgba32)] - public void NoOutputSubfolderIsPresentByDefault(TestImageProvider provider) - where TPixel : struct, IPixel => Assert.Empty(provider.Utility.OutputSubfolderName); + /// + /// Need to us to create instance of when pixelType is StandardImageClass + /// + /// + /// + /// + public static Image CreateTestImage() + where TPixel : struct, IPixel => + new Image(3, 3); [Theory] - [WithBlankImages(42, 666, PixelTypes.Rgba32 | PixelTypes.Argb32 | PixelTypes.HalfSingle, "hello")] - public void Use_WithEmptyImageAttribute(TestImageProvider provider, string message) + [MemberData(nameof(BasicData))] + public void Blank_MemberData(TestImageProvider provider) where TPixel : struct, IPixel { Image img = provider.GetImage(); - Assert.Equal(42, img.Width); - Assert.Equal(666, img.Height); - Assert.Equal("hello", message); + Assert.True(img.Width * img.Height > 0); } [Theory] - [WithBlankImages(42, 666, PixelTypes.All, "hello")] - public void Use_WithBlankImagesAttribute_WithAllPixelTypes( - TestImageProvider provider, - string message) + [MemberData(nameof(FileData))] + public void File_MemberData(TestImageProvider provider) where TPixel : struct, IPixel { + this.Output.WriteLine("SRC: " + provider.Utility.SourceFileOrDescription); + this.Output.WriteLine("OUT: " + provider.Utility.GetTestOutputFileName()); + Image img = provider.GetImage(); - Assert.Equal(42, img.Width); - Assert.Equal(666, img.Height); - Assert.Equal("hello", message); + Assert.True(img.Width * img.Height > 0); } [Theory] - [WithBlankImages(1, 1, PixelTypes.Rgba32, PixelTypes.Rgba32)] - [WithBlankImages(1, 1, PixelTypes.Alpha8, PixelTypes.Alpha8)] - [WithBlankImages(1, 1, PixelTypes.Argb32, PixelTypes.Argb32)] - public void PixelType_PropertyValueIsCorrect(TestImageProvider provider, PixelTypes expected) - where TPixel : struct, IPixel => Assert.Equal(expected, provider.PixelType); - - [Theory] - [WithFile(TestImages.Bmp.Car, PixelTypes.All, 88)] - [WithFile(TestImages.Bmp.F, PixelTypes.All, 88)] - public void Use_WithFileAttribute(TestImageProvider provider, int yo) + [WithFile(TestImages.Bmp.F, PixelTypes.Rgba32)] + public void GetImage_WithCustomParameterlessDecoder_ShouldUtilizeCache( + TestImageProvider provider) where TPixel : struct, IPixel { + if (!TestEnvironment.Is64BitProcess) + { + // We don't cache with the 32 bit build. + return; + } + Assert.NotNull(provider.Utility.SourceFileOrDescription); - Image img = provider.GetImage(); - Assert.True(img.Width * img.Height > 0); - Assert.Equal(88, yo); + TestDecoder.DoTestThreadSafe( + () => + { + string testName = nameof(this.GetImage_WithCustomParameterlessDecoder_ShouldUtilizeCache); - string fn = provider.Utility.GetTestOutputFileName("jpg"); - this.Output.WriteLine(fn); - } + var decoder = new TestDecoder(); + decoder.InitCaller(testName); - private class TestDecoder : IImageDecoder - { - public Image Decode(Configuration configuration, Stream stream) - where TPixel : struct, IPixel - { - invocationCounts[this.callerName]++; - return new Image(42, 42); - } + provider.GetImage(decoder); + Assert.Equal(1, TestDecoder.GetInvocationCount(testName)); - // Couldn't make xUnit happy without this hackery: + provider.GetImage(decoder); + Assert.Equal(1, TestDecoder.GetInvocationCount(testName)); + }); + } - private static readonly ConcurrentDictionary invocationCounts = new ConcurrentDictionary(); + [Theory] + [WithFile(TestImages.Bmp.F, PixelTypes.Rgba32)] + public void GetImage_WithCustomParametricDecoder_ShouldNotUtilizeCache_WhenParametersAreNotEqual( + TestImageProvider provider) + where TPixel : struct, IPixel + { + Assert.NotNull(provider.Utility.SourceFileOrDescription); - private string callerName = null; + TestDecoderWithParameters.DoTestThreadSafe( + () => + { + string testName = nameof(this + .GetImage_WithCustomParametricDecoder_ShouldNotUtilizeCache_WhenParametersAreNotEqual); - internal void InitCaller(string name) - { - this.callerName = name; - invocationCounts[name] = 0; - } + var decoder1 = new TestDecoderWithParameters() { Param1 = "Lol", Param2 = 42 }; + decoder1.InitCaller(testName); - internal static int GetInvocationCount(string callerName) => invocationCounts[callerName]; + var decoder2 = new TestDecoderWithParameters() { Param1 = "LoL", Param2 = 42 }; + decoder2.InitCaller(testName); - private static readonly object Monitor = new object(); + provider.GetImage(decoder1); + Assert.Equal(1, TestDecoderWithParameters.GetInvocationCount(testName)); - public static void DoTestThreadSafe(Action action) - { - lock (Monitor) - { - action(); - } - } + provider.GetImage(decoder2); + Assert.Equal(2, TestDecoderWithParameters.GetInvocationCount(testName)); + }); } [Theory] [WithFile(TestImages.Bmp.F, PixelTypes.Rgba32)] - public void GetImage_WithCustomParameterlessDecoder_ShouldUtilizeCache(TestImageProvider provider) + public void GetImage_WithCustomParametricDecoder_ShouldUtilizeCache_WhenParametersAreEqual( + TestImageProvider provider) where TPixel : struct, IPixel { if (!TestEnvironment.Is64BitProcess) @@ -120,121 +143,122 @@ namespace SixLabors.ImageSharp.Tests Assert.NotNull(provider.Utility.SourceFileOrDescription); - TestDecoder.DoTestThreadSafe(() => - { - string testName = nameof(this.GetImage_WithCustomParameterlessDecoder_ShouldUtilizeCache); - - var decoder = new TestDecoder(); - decoder.InitCaller(testName); - - provider.GetImage(decoder); - Assert.Equal(1, TestDecoder.GetInvocationCount(testName)); + TestDecoderWithParameters.DoTestThreadSafe( + () => + { + string testName = nameof(this + .GetImage_WithCustomParametricDecoder_ShouldUtilizeCache_WhenParametersAreEqual); - provider.GetImage(decoder); - Assert.Equal(1, TestDecoder.GetInvocationCount(testName)); - }); - } + var decoder1 = new TestDecoderWithParameters() { Param1 = "Lol", Param2 = 666 }; + decoder1.InitCaller(testName); - private class TestDecoderWithParameters : IImageDecoder - { - public string Param1 { get; set; } + var decoder2 = new TestDecoderWithParameters() { Param1 = "Lol", Param2 = 666 }; + decoder2.InitCaller(testName); - public int Param2 { get; set; } + provider.GetImage(decoder1); + Assert.Equal(1, TestDecoderWithParameters.GetInvocationCount(testName)); - public Image Decode(Configuration configuration, Stream stream) - where TPixel : struct, IPixel - { - invocationCounts[this.callerName]++; - return new Image(42, 42); - } + provider.GetImage(decoder2); + Assert.Equal(1, TestDecoderWithParameters.GetInvocationCount(testName)); + }); + } - private static readonly ConcurrentDictionary invocationCounts = new ConcurrentDictionary(); + [Theory] + [WithBlankImages(1, 1, PixelTypes.Rgba32)] + public void NoOutputSubfolderIsPresentByDefault(TestImageProvider provider) + where TPixel : struct, IPixel => + Assert.Empty(provider.Utility.OutputSubfolderName); - private string callerName = null; + [Theory] + [WithBlankImages(1, 1, PixelTypes.Rgba32, PixelTypes.Rgba32)] + [WithBlankImages(1, 1, PixelTypes.Alpha8, PixelTypes.Alpha8)] + [WithBlankImages(1, 1, PixelTypes.Argb32, PixelTypes.Argb32)] + public void PixelType_PropertyValueIsCorrect(TestImageProvider provider, PixelTypes expected) + where TPixel : struct, IPixel => + Assert.Equal(expected, provider.PixelType); - internal void InitCaller(string name) + [Theory] + [WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32)] + public void SaveTestOutputFileMultiFrame(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) { - this.callerName = name; - invocationCounts[name] = 0; - } - - internal static int GetInvocationCount(string callerName) => invocationCounts[callerName]; - - private static readonly object Monitor = new object(); + string[] files = provider.Utility.SaveTestOutputFileMultiFrame(image); - public static void DoTestThreadSafe(Action action) - { - lock (Monitor) + Assert.True(files.Length > 2); + foreach (string path in files) { - action(); + this.Output.WriteLine(path); + Assert.True(File.Exists(path)); } } } [Theory] - [WithFile(TestImages.Bmp.F, PixelTypes.Rgba32)] - public void GetImage_WithCustomParametricDecoder_ShouldUtilizeCache_WhenParametersAreEqual(TestImageProvider provider) + [WithBasicTestPatternImages(50, 100, PixelTypes.Rgba32)] + [WithBasicTestPatternImages(49, 17, PixelTypes.Rgba32)] + [WithBasicTestPatternImages(20, 10, PixelTypes.Rgba32)] + public void Use_WithBasicTestPatternImages(TestImageProvider provider) where TPixel : struct, IPixel { - if (!TestEnvironment.Is64BitProcess) + using (Image img = provider.GetImage()) { - // We don't cache with the 32 bit build. - return; + img.DebugSave(provider); } + } - Assert.NotNull(provider.Utility.SourceFileOrDescription); - - TestDecoderWithParameters.DoTestThreadSafe(() => - { - string testName = - nameof(this.GetImage_WithCustomParametricDecoder_ShouldUtilizeCache_WhenParametersAreEqual); - - var decoder1 = new TestDecoderWithParameters() { Param1 = "Lol", Param2 = 666 }; - decoder1.InitCaller(testName); + [Theory] + [WithBlankImages(42, 666, PixelTypes.All, "hello")] + public void Use_WithBlankImagesAttribute_WithAllPixelTypes( + TestImageProvider provider, + string message) + where TPixel : struct, IPixel + { + Image img = provider.GetImage(); - var decoder2 = new TestDecoderWithParameters() { Param1 = "Lol", Param2 = 666 }; - decoder2.InitCaller(testName); + Assert.Equal(42, img.Width); + Assert.Equal(666, img.Height); + Assert.Equal("hello", message); + } - provider.GetImage(decoder1); - Assert.Equal(1, TestDecoderWithParameters.GetInvocationCount(testName)); + [Theory] + [WithBlankImages(42, 666, PixelTypes.Rgba32 | PixelTypes.Argb32 | PixelTypes.HalfSingle, "hello")] + public void Use_WithEmptyImageAttribute(TestImageProvider provider, string message) + where TPixel : struct, IPixel + { + Image img = provider.GetImage(); - provider.GetImage(decoder2); - Assert.Equal(1, TestDecoderWithParameters.GetInvocationCount(testName)); - }); + Assert.Equal(42, img.Width); + Assert.Equal(666, img.Height); + Assert.Equal("hello", message); } [Theory] - [WithFile(TestImages.Bmp.F, PixelTypes.Rgba32)] - public void GetImage_WithCustomParametricDecoder_ShouldNotUtilizeCache_WhenParametersAreNotEqual(TestImageProvider provider) + [WithFile(TestImages.Bmp.Car, PixelTypes.All, 123)] + [WithFile(TestImages.Bmp.F, PixelTypes.All, 123)] + public void Use_WithFileAttribute(TestImageProvider provider, int yo) where TPixel : struct, IPixel { Assert.NotNull(provider.Utility.SourceFileOrDescription); - - TestDecoderWithParameters.DoTestThreadSafe(() => + using (Image img = provider.GetImage()) { - string testName = - nameof(this.GetImage_WithCustomParametricDecoder_ShouldNotUtilizeCache_WhenParametersAreNotEqual); - - var decoder1 = new TestDecoderWithParameters() { Param1 = "Lol", Param2 = 42 }; - decoder1.InitCaller(testName); - - var decoder2 = new TestDecoderWithParameters() { Param1 = "LoL", Param2 = 42 }; - decoder2.InitCaller(testName); + Assert.True(img.Width * img.Height > 0); - provider.GetImage(decoder1); - Assert.Equal(1, TestDecoderWithParameters.GetInvocationCount(testName)); + Assert.Equal(123, yo); - provider.GetImage(decoder2); - Assert.Equal(2, TestDecoderWithParameters.GetInvocationCount(testName)); - }); + string fn = provider.Utility.GetTestOutputFileName("jpg"); + this.Output.WriteLine(fn); + } } - - public static string[] AllBmpFiles = - { - TestImages.Bmp.F, - TestImages.Bmp.Bit8 - }; + [Theory] + [WithFile(TestImages.Jpeg.Baseline.Testorig420, PixelTypes.Rgba32)] + public void Use_WithFileAttribute_CustomConfig(TestImageProvider provider) + where TPixel : struct, IPixel + { + EnsureCustomConfigurationIsApplied(provider); + } [Theory] [WithFileCollection(nameof(AllBmpFiles), PixelTypes.Rgba32 | PixelTypes.Argb32)] @@ -249,20 +273,15 @@ namespace SixLabors.ImageSharp.Tests } [Theory] - [WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32)] - public void SaveTestOutputFileMultiFrame(TestImageProvider provider) + [WithMemberFactory(nameof(CreateTestImage), PixelTypes.All)] + public void Use_WithMemberFactoryAttribute(TestImageProvider provider) where TPixel : struct, IPixel { - using (Image image = provider.GetImage()) + Image img = provider.GetImage(); + Assert.Equal(3, img.Width); + if (provider.PixelType == PixelTypes.Rgba32) { - string[] files = provider.Utility.SaveTestOutputFileMultiFrame(image); - - Assert.True(files.Length > 2); - foreach (string path in files) - { - this.Output.WriteLine(path); - Assert.True(File.Exists(path)); - } + Assert.IsType>(img); } } @@ -291,89 +310,112 @@ namespace SixLabors.ImageSharp.Tests } } - /// - /// Need to us to create instance of when pixelType is StandardImageClass - /// - /// - /// - /// - public static Image CreateTestImage() - where TPixel : struct, IPixel => new Image(3, 3); - [Theory] - [WithMemberFactory(nameof(CreateTestImage), PixelTypes.All)] - public void Use_WithMemberFactoryAttribute(TestImageProvider provider) + [WithTestPatternImages(49, 20, PixelTypes.Rgba32)] + public void Use_WithTestPatternImages(TestImageProvider provider) where TPixel : struct, IPixel { - Image img = provider.GetImage(); - Assert.Equal(3, img.Width); - if (provider.PixelType == PixelTypes.Rgba32) + using (Image img = provider.GetImage()) { - Assert.IsType>(img); + img.DebugSave(provider); } - } - + [Theory] - [WithTestPatternImages(49,20, PixelTypes.Rgba32)] - public void Use_WithTestPatternImages(TestImageProvider provider) + [WithTestPatternImages(20, 20, PixelTypes.Rgba32)] + public void Use_WithTestPatternImages_CustomConfiguration(TestImageProvider provider) where TPixel : struct, IPixel { - using (Image img = provider.GetImage()) - { - img.DebugSave(provider); - } + EnsureCustomConfigurationIsApplied(provider); } - [Theory] - [WithBasicTestPatternImages(50, 100, PixelTypes.Rgba32)] - [WithBasicTestPatternImages(49,17, PixelTypes.Rgba32)] - [WithBasicTestPatternImages(20, 10, PixelTypes.Rgba32)] - public void Use_WithBasicTestPatternImages(TestImageProvider provider) + private static void EnsureCustomConfigurationIsApplied(TestImageProvider provider) where TPixel : struct, IPixel { - using (Image img = provider.GetImage()) + using (var image1 = provider.GetImage()) { - img.DebugSave(provider); + var customConfiguration = Configuration.CreateDefaultInstance(); + provider.Configuration = customConfiguration; + + using (var image2 = provider.GetImage()) + using (var image3 = provider.GetImage()) + { + Assert.Same(customConfiguration, image2.GetConfiguration()); + Assert.Same(customConfiguration, image3.GetConfiguration()); + } } } - - public static readonly TheoryData BasicData = new TheoryData() + private class TestDecoder : IImageDecoder { - TestImageProvider.Blank(10, 20), - TestImageProvider.Blank( - 10, - 20), - }; + // Couldn't make xUnit happy without this hackery: - [Theory] - [MemberData(nameof(BasicData))] - public void Blank_MemberData(TestImageProvider provider) - where TPixel : struct, IPixel - { - Image img = provider.GetImage(); + private static readonly ConcurrentDictionary invocationCounts = + new ConcurrentDictionary(); - Assert.True(img.Width * img.Height > 0); + private static readonly object Monitor = new object(); + + private string callerName = null; + + public static void DoTestThreadSafe(Action action) + { + lock (Monitor) + { + action(); + } + } + + public Image Decode(Configuration configuration, Stream stream) + where TPixel : struct, IPixel + { + invocationCounts[this.callerName]++; + return new Image(42, 42); + } + + internal static int GetInvocationCount(string callerName) => invocationCounts[callerName]; + + internal void InitCaller(string name) + { + this.callerName = name; + invocationCounts[name] = 0; + } } - public static readonly TheoryData FileData = new TheoryData() + private class TestDecoderWithParameters : IImageDecoder { - TestImageProvider.File(TestImages.Bmp.Car), - TestImageProvider.File(TestImages.Bmp.F) - }; + private static readonly ConcurrentDictionary invocationCounts = + new ConcurrentDictionary(); - [Theory] - [MemberData(nameof(FileData))] - public void File_MemberData(TestImageProvider provider) - where TPixel : struct, IPixel - { - this.Output.WriteLine("SRC: " + provider.Utility.SourceFileOrDescription); - this.Output.WriteLine("OUT: " + provider.Utility.GetTestOutputFileName()); + private static readonly object Monitor = new object(); - Image img = provider.GetImage(); + private string callerName = null; - Assert.True(img.Width * img.Height > 0); + public string Param1 { get; set; } + + public int Param2 { get; set; } + + public static void DoTestThreadSafe(Action action) + { + lock (Monitor) + { + action(); + } + } + + public Image Decode(Configuration configuration, Stream stream) + where TPixel : struct, IPixel + { + invocationCounts[this.callerName]++; + return new Image(42, 42); + } + + internal static int GetInvocationCount(string callerName) => invocationCounts[callerName]; + + internal void InitCaller(string name) + { + this.callerName = name; + invocationCounts[name] = 0; + } } } -} +} \ No newline at end of file diff --git a/tests/Images/External b/tests/Images/External index c7333d2a81..6db7ed95dd 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit c7333d2a81a74d1936bd202bcb6b16cbfe6bcdce +Subproject commit 6db7ed95dd8a2de4caa6d885952b6689d026ad27 From 1612f495e2ac3ef571412758b4c802cd0727f645 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 19 Apr 2019 21:15:50 +0200 Subject: [PATCH 075/223] more robust tests --- .../Processing/Processors/Transforms/ResizeTests.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs index 2ffb42916f..6d02b4b458 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs @@ -112,11 +112,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms Assert.NotEmpty(allocator.AllocationLog); - var internalAllocations = allocator.AllocationLog.Where( - e => e.ElementType == typeof(Vector4)).ToArray(); - - int maxAllocationSize = internalAllocations.Max(e => e.LengthInBytes); - + int maxAllocationSize = allocator.AllocationLog.Where( + e => e.ElementType == typeof(Vector4)).Max(e => e.LengthInBytes); Assert.True(maxAllocationSize <= Math.Max(workingBufferSizeHintInBytes, minimumWorkerAllocationInBytes)); } @@ -256,6 +253,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms var comparer = ImageComparer.TolerantPercentage(allowHigherInaccuracy ? 0.3f : 0.017f); + // Let's make the working buffer size non-default: + provider.Configuration.WorkingBufferSizeHintInBytes = 16 * 1024 * SizeOfVector4; + provider.RunValidatingProcessorTest( ctx => { From eb08150e6182fa930bbbd69f595f3f892f2ba4f9 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 19 Apr 2019 21:42:39 +0200 Subject: [PATCH 076/223] ResizeTests.LargeImage --- .../Transforms/Resize/ResizeWorker.cs | 7 ++++--- .../Processors/Transforms/ResizeTests.cs | 17 ++++++++++++++++- tests/Images/External | 2 +- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs index ce28a00ee7..9282c21283 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs @@ -114,7 +114,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms // Ensure offsets are normalized for cropping and padding. ResizeKernel kernel = this.verticalKernelMap.GetKernel(y - this.targetOrigin.Y); - while (kernel.StartIndex + kernel.Length > this.currentWindow.Max) + if (kernel.StartIndex + kernel.Length > this.currentWindow.Max) { this.Slide(); } @@ -145,8 +145,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms private void Slide() { - int minY = this.currentWindow.Min + this.windowBandHeight; - int maxY = Math.Min(this.currentWindow.Max + this.windowBandHeight, this.sourceRectangle.Height); + int minY = this.currentWindow.Max - this.windowBandHeight; + int maxY = Math.Min(minY + this.workerHeight, this.sourceRectangle.Height); + this.currentWindow = new RowInterval(minY, maxY); this.CalculateFirstPassValues(this.currentWindow); } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs index 6d02b4b458..f4098aaf7e 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs @@ -34,12 +34,27 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms public static readonly string[] SmokeTestResamplerNames = { - nameof(KnownResamplers.NearestNeighbor), nameof(KnownResamplers.Bicubic), nameof(KnownResamplers.Box), + nameof(KnownResamplers.NearestNeighbor), + nameof(KnownResamplers.Bicubic), + nameof(KnownResamplers.Box), nameof(KnownResamplers.Lanczos5), }; private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.07F); + [Theory] + [WithTestPatternImages(4000, 4000, PixelTypes.Rgba32)] + public void LargeImage(TestImageProvider provider) + where TPixel : struct, IPixel + { + if (!TestEnvironment.Is64BitProcess) + { + return; + } + + provider.RunValidatingProcessorTest(x => x.Resize(300, 300), appendPixelTypeToFileName: false); + } + [Theory] [WithBasicTestPatternImages(15, 12, PixelTypes.Rgba32, 2, 3, 1, 2)] [WithBasicTestPatternImages(2, 256, PixelTypes.Rgba32, 1, 1, 1, 8)] diff --git a/tests/Images/External b/tests/Images/External index 6db7ed95dd..8693e2fd45 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 6db7ed95dd8a2de4caa6d885952b6689d026ad27 +Subproject commit 8693e2fd4577a9ac1a749da8db564095b5a05389 From c8bd3ce9f7524c5ae3093d801deb8c96d438d2d3 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 19 Apr 2019 22:52:14 +0200 Subject: [PATCH 077/223] optimized sliding works! --- src/ImageSharp/Memory/Buffer2DExtensions.cs | 28 ++++++++++++++++--- .../Transforms/Resize/ResizeWorker.cs | 17 ++++++++--- .../ImageSharp.Tests/Memory/Buffer2DTests.cs | 23 +++++++++++++++ 3 files changed, 60 insertions(+), 8 deletions(-) diff --git a/src/ImageSharp/Memory/Buffer2DExtensions.cs b/src/ImageSharp/Memory/Buffer2DExtensions.cs index c8bbf01c7f..61fcb99db2 100644 --- a/src/ImageSharp/Memory/Buffer2DExtensions.cs +++ b/src/ImageSharp/Memory/Buffer2DExtensions.cs @@ -3,7 +3,9 @@ using System; using System.Buffers; +using System.Diagnostics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using SixLabors.Primitives; @@ -27,8 +29,8 @@ namespace SixLabors.ImageSharp.Memory { DebugGuard.NotNull(buffer, nameof(buffer)); DebugGuard.MustBeGreaterThanOrEqualTo(sourceIndex, 0, nameof(sourceIndex)); - DebugGuard.MustBeGreaterThanOrEqualTo(destIndex, sourceIndex + columnCount, nameof(destIndex)); - DebugGuard.MustBeLessThanOrEqualTo(destIndex, buffer.Width - columnCount, nameof(destIndex)); + DebugGuard.MustBeGreaterThanOrEqualTo(destIndex, 0, nameof(sourceIndex)); + CheckColumnRegionsDoNotOverlap(buffer, sourceIndex, destIndex, columnCount); int elementSize = Unsafe.SizeOf(); int width = buffer.Width * elementSize; @@ -36,9 +38,11 @@ namespace SixLabors.ImageSharp.Memory int dOffset = destIndex * elementSize; long count = columnCount * elementSize; - using (MemoryHandle handle = buffer.Memory.Pin()) + Span span = MemoryMarshal.AsBytes(buffer.Memory.Span); + + fixed (byte* ptr = span) { - byte* basePtr = (byte*)handle.Pointer; + byte* basePtr = (byte*)ptr; for (int y = 0; y < buffer.Height; y++) { byte* sPtr = basePtr + sOffset; @@ -164,5 +168,21 @@ namespace SixLabors.ImageSharp.Memory { return buffer.MemorySource.GetSpan(); } + + [Conditional("DEBUG")] + private static void CheckColumnRegionsDoNotOverlap( + Buffer2D buffer, + int sourceIndex, + int destIndex, + int columnCount) + where T : struct + { + int minIndex = Math.Min(sourceIndex, destIndex); + int maxIndex = Math.Max(sourceIndex, destIndex); + if (maxIndex < minIndex + columnCount || maxIndex > buffer.Width - columnCount) + { + throw new InvalidOperationException("Column regions should not overlap!"); + } + } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs index 9282c21283..ed505e2219 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs @@ -148,14 +148,23 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms int minY = this.currentWindow.Max - this.windowBandHeight; int maxY = Math.Min(minY + this.workerHeight, this.sourceRectangle.Height); + // Copy previous bottom band to the new top: + // (rows <--> columns, because the buffer is transposed) + this.transposedFirstPassBuffer.CopyColumns( + this.workerHeight - this.windowBandHeight, + 0, + this.windowBandHeight); + this.currentWindow = new RowInterval(minY, maxY); - this.CalculateFirstPassValues(this.currentWindow); + + // Calculate the remainder: + this.CalculateFirstPassValues(this.currentWindow.Slice(this.windowBandHeight)); } - private void CalculateFirstPassValues(RowInterval window) + private void CalculateFirstPassValues(RowInterval calculationInterval) { Span tempRowSpan = this.tempRowBuffer.GetSpan(); - for (int y = window.Min; y < window.Max; y++) + for (int y = calculationInterval.Min; y < calculationInterval.Max; y++) { Span sourceRow = this.source.GetRowSpan(y); @@ -166,7 +175,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms this.conversionModifiers); // ref Vector4 firstPassBaseRef = ref this.buffer.Span[y - top]; - Span firstPassSpan = this.transposedFirstPassBuffer.Span.Slice(y - window.Min); + Span firstPassSpan = this.transposedFirstPassBuffer.Span.Slice(y - this.currentWindow.Min); for (int x = this.targetWorkingRect.Left; x < this.targetWorkingRect.Right; x++) { diff --git a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs index 3d40875461..4af3b81e20 100644 --- a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs +++ b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs @@ -155,5 +155,28 @@ namespace SixLabors.ImageSharp.Tests.Memory } } } + + [Fact] + public void CopyColumns_InvokeMultipleTimes() + { + Random rnd = new Random(123); + using (Buffer2D b = this.MemoryAllocator.Allocate2D(100, 100)) + { + rnd.RandomFill(b.Span, 0, 1); + + b.CopyColumns(0, 50, 22); + b.CopyColumns(0, 50, 22); + + for (int y = 0; y < b.Height; y++) + { + Span row = b.GetRowSpan(y); + + Span s = row.Slice(0, 22); + Span d = row.Slice(50, 22); + + Xunit.Assert.True(s.SequenceEqual(d)); + } + } + } } } \ No newline at end of file From ea2682ff329f292da0f6a3852bf317cf294d7760 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 19 Apr 2019 23:02:10 +0200 Subject: [PATCH 078/223] reapply unsafe optimizations --- .../Processors/Transforms/Resize/ResizeKernel.cs | 8 ++++---- .../Processors/Transforms/Resize/ResizeWorker.cs | 13 +++++++------ 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs index 71cba90564..dce4e70d62 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs @@ -55,11 +55,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms [MethodImpl(InliningOptions.ShortMethod)] public Vector4 Convolve(Span rowSpan) { - return this.ConvolveCore(rowSpan.Slice(this.StartIndex)); + return this.ConvolveCore(ref rowSpan[this.StartIndex]); } [MethodImpl(InliningOptions.ShortMethod)] - public Vector4 ConvolveCore(Span offsetedRowSpan) + public Vector4 ConvolveCore(ref Vector4 rowStartRef) { ref float horizontalValues = ref Unsafe.AsRef(this.bufferPtr); @@ -70,8 +70,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms { float weight = Unsafe.Add(ref horizontalValues, i); - // Vector4 v = Unsafe.Add(ref rowStartRef, i); - Vector4 v = offsetedRowSpan[i]; + // Vector4 v = offsetedRowSpan[i]; + Vector4 v = Unsafe.Add(ref rowStartRef, i); result += v * weight; } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs index ed505e2219..9b7d1d17c6 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs @@ -125,10 +125,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms for (int x = 0; x < this.destWidth; x++) { - Span firstPassColumn = this.GetColumnSpan(x).Slice(top); + // Span firstPassColumn = this.GetColumnSpan(x).Slice(top); + ref Vector4 firstPassColumnBase = ref this.GetColumnSpan(x)[top]; // Destination color components - Unsafe.Add(ref tempRowBase, x) = kernel.ConvolveCore(firstPassColumn); + Unsafe.Add(ref tempRowBase, x) = kernel.ConvolveCore(ref firstPassColumnBase); } Span targetRowSpan = destination.GetRowSpan(y); @@ -174,15 +175,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms tempRowSpan, this.conversionModifiers); - // ref Vector4 firstPassBaseRef = ref this.buffer.Span[y - top]; - Span firstPassSpan = this.transposedFirstPassBuffer.Span.Slice(y - this.currentWindow.Min); + // Span firstPassSpan = this.transposedFirstPassBuffer.Span.Slice(y - this.currentWindow.Min); + ref Vector4 firstPassBaseRef = ref this.transposedFirstPassBuffer.Span[y - this.currentWindow.Min]; for (int x = this.targetWorkingRect.Left; x < this.targetWorkingRect.Right; x++) { ResizeKernel kernel = this.horizontalKernelMap.GetKernel(x - this.targetOrigin.X); - firstPassSpan[x * this.workerHeight] = kernel.Convolve(tempRowSpan); - // Unsafe.Add(ref firstPassBaseRef, x * this.sourceRectangle.Height) = kernel.Convolve(tempRowSpan); + // firstPassSpan[x * this.workerHeight] = kernel.Convolve(tempRowSpan); + Unsafe.Add(ref firstPassBaseRef, x * this.workerHeight) = kernel.Convolve(tempRowSpan); } } } From c10c6c311136e608bb8ac3ec2c39864f900903a1 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 19 Apr 2019 23:07:32 +0200 Subject: [PATCH 079/223] moar unsafe optimization --- .../Processing/Processors/Transforms/Resize/ResizeWorker.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs index 9b7d1d17c6..7fe2638802 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs @@ -123,10 +123,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms int top = kernel.StartIndex - this.currentWindow.Min; + ref Vector4 fpBase = ref this.transposedFirstPassBuffer.Span[top]; + for (int x = 0; x < this.destWidth; x++) { // Span firstPassColumn = this.GetColumnSpan(x).Slice(top); - ref Vector4 firstPassColumnBase = ref this.GetColumnSpan(x)[top]; + // ref Vector4 firstPassColumnBase = ref this.GetColumnSpan(x)[top]; + ref Vector4 firstPassColumnBase = ref Unsafe.Add(ref fpBase, x * this.workerHeight); // Destination color components Unsafe.Add(ref tempRowBase, x) = kernel.ConvolveCore(ref firstPassColumnBase); From c80501a6e99bb8626affb3d226aaa09731df1ffc Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 20 Apr 2019 00:08:29 +0200 Subject: [PATCH 080/223] benchmark WorkingBufferSizeHint effects --- .../ImageSharp.Benchmarks/Samplers/Resize.cs | 61 +++++++++++++------ .../Processors/Transforms/ResizeTests.cs | 18 ++++-- 2 files changed, 57 insertions(+), 22 deletions(-) diff --git a/tests/ImageSharp.Benchmarks/Samplers/Resize.cs b/tests/ImageSharp.Benchmarks/Samplers/Resize.cs index 51f3a5653a..cb2481dbf1 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/Resize.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/Resize.cs @@ -4,6 +4,7 @@ using System; using System.Drawing; using System.Drawing.Drawing2D; +using System.Globalization; using BenchmarkDotNet.Attributes; @@ -23,15 +24,21 @@ namespace SixLabors.ImageSharp.Benchmarks private Bitmap sourceBitmap; - [Params(3032)] - public int SourceSize { get; set; } + [Params("3032-400")] + public virtual string SourceToDest { get; set; } + + protected int SourceSize { get; private set; } + + protected int DestSize { get; private set; } - [Params(400)] - public int DestSize { get; set; } [GlobalSetup] - public void Setup() + public virtual void Setup() { + string[] stuff = this.SourceToDest.Split('-'); + this.SourceSize = int.Parse(stuff[0], CultureInfo.InvariantCulture); + this.DestSize = int.Parse(stuff[1], CultureInfo.InvariantCulture); + this.sourceImage = new Image(this.Configuration, this.SourceSize, this.SourceSize); this.sourceBitmap = new Bitmap(this.SourceSize, this.SourceSize); } @@ -95,26 +102,44 @@ namespace SixLabors.ImageSharp.Benchmarks ctx.Resize(this.DestSize, this.DestSize, KnownResamplers.Bicubic); } - // RESULTS (2019 April): + // RESULTS - 2019 April - ResizeWorker: // - // BenchmarkDotNet=v0.11.3, OS=Windows 10.0.17134.648 (1803/April2018Update/Redstone4) + // BenchmarkDotNet=v0.11.3, OS=Windows 10.0.17134.706 (1803/April2018Update/Redstone4) // Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores - // Frequency=2742192 Hz, Resolution=364.6718 ns, Timer=TSC - // .NET Core SDK=2.1.602 + // Frequency=2742189 Hz, Resolution=364.6722 ns, Timer=TSC + // .NET Core SDK=2.2.202 // [Host] : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT - // Clr : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3362.0 + // Clr : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3394.0 // Core : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT // - // IterationCount=3 LaunchCount=1 WarmupCount=3 + // IterationCount=3 LaunchCount=1 WarmupCount=3 // - // Method | Job | Runtime | SourceSize | DestSize | Mean | Error | StdDev | Ratio | Gen 0/1k Op | Gen 1/1k Op | Gen 2/1k Op | Allocated Memory/Op | - // ----------------------------------------- |----- |-------- |----------- |--------- |----------:|----------:|----------:|------:|------------:|------------:|------------:|--------------------:| - // SystemDrawing | Clr | Clr | 3032 | 400 | 118.71 ms | 4.884 ms | 0.2677 ms | 1.00 | - | - | - | 2048 B | - // 'ImageSharp, MaxDegreeOfParallelism = 1' | Clr | Clr | 3032 | 400 | 94.55 ms | 16.160 ms | 0.8858 ms | 0.80 | - | - | - | 16384 B | - // | | | | | | | | | | | | | - // SystemDrawing | Core | Core | 3032 | 400 | 118.38 ms | 2.814 ms | 0.1542 ms | 1.00 | - | - | - | 96 B | - // 'ImageSharp, MaxDegreeOfParallelism = 1' | Core | Core | 3032 | 400 | 90.28 ms | 4.679 ms | 0.2565 ms | 0.76 | - | - | - | 15712 B | + // Method | Job | Runtime | SourceToDest | Mean | Error | StdDev | Ratio | RatioSD | Gen 0/1k Op | Gen 1/1k Op | Gen 2/1k Op | Allocated Memory/Op | + // ----------------------------------------- |----- |-------- |------------- |----------:|----------:|----------:|------:|--------:|------------:|------------:|------------:|--------------------:| + // SystemDrawing | Clr | Clr | 3032-400 | 120.11 ms | 1.435 ms | 0.0786 ms | 1.00 | 0.00 | - | - | - | 1638 B | + // 'ImageSharp, MaxDegreeOfParallelism = 1' | Clr | Clr | 3032-400 | 75.32 ms | 34.143 ms | 1.8715 ms | 0.63 | 0.02 | - | - | - | 16384 B | + // | | | | | | | | | | | | | + // SystemDrawing | Core | Core | 3032-400 | 120.33 ms | 6.669 ms | 0.3656 ms | 1.00 | 0.00 | - | - | - | 96 B | + // 'ImageSharp, MaxDegreeOfParallelism = 1' | Core | Core | 3032-400 | 88.56 ms | 1.864 ms | 0.1022 ms | 0.74 | 0.00 | - | - | - | 15568 B | + } + + /// + /// Is it worth to set a larger working buffer limit for resize? + /// Conclusion: It doesn't really have an effect. + /// + public class Resize_Bicubic_Rgba32_CompareWorkBufferSizes : Resize_Bicubic_Rgba32 + { + [Params(128, 512, 1024, 8 * 1024)] + public int WorkingBufferSizeHintInKilobytes { get; set; } + + [Params("3032-400", "4000-300")] + public override string SourceToDest { get; set; } + public override void Setup() + { + this.Configuration.WorkingBufferSizeHintInBytes = this.WorkingBufferSizeHintInKilobytes * 1024; + base.Setup(); + } } public class Resize_Bicubic_Bgra32 : ResizeBenchmarkBase diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs index f4098aaf7e..6bc2321de5 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs @@ -42,9 +42,13 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.07F); - [Theory] - [WithTestPatternImages(4000, 4000, PixelTypes.Rgba32)] - public void LargeImage(TestImageProvider provider) + [Theory( + Skip = "Debug only, enable manually" + )] + [WithTestPatternImages(4000, 4000, PixelTypes.Rgba32, 300, 1024)] + [WithTestPatternImages(3032, 3032, PixelTypes.Rgba32, 400, 1024)] + [WithTestPatternImages(3032, 3032, PixelTypes.Rgba32, 400, 128)] + public void LargeImage(TestImageProvider provider, int destSize, int workingBufferSizeHintInKilobytes) where TPixel : struct, IPixel { if (!TestEnvironment.Is64BitProcess) @@ -52,7 +56,13 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms return; } - provider.RunValidatingProcessorTest(x => x.Resize(300, 300), appendPixelTypeToFileName: false); + provider.Configuration.WorkingBufferSizeHintInBytes = workingBufferSizeHintInKilobytes * 1024; + + using (var image = provider.GetImage()) + { + image.Mutate(x => x.Resize(destSize, destSize)); + image.DebugSave(provider, appendPixelTypeToFileName: false); + } } [Theory] From 0a6a085e91df9bc1a791b4efcf3d27616994a5ee Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 20 Apr 2019 00:36:26 +0200 Subject: [PATCH 081/223] memory profiling with Sandbox46 --- tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj | 2 +- tests/ImageSharp.Sandbox46/Program.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj index cb286cc286..6569dc002e 100644 --- a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj +++ b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj @@ -1,7 +1,7 @@  Exe - net461 + net472 win7-x64 True false diff --git a/tests/ImageSharp.Sandbox46/Program.cs b/tests/ImageSharp.Sandbox46/Program.cs index 02d4f80c55..afe7eb04ff 100644 --- a/tests/ImageSharp.Sandbox46/Program.cs +++ b/tests/ImageSharp.Sandbox46/Program.cs @@ -33,11 +33,11 @@ namespace SixLabors.ImageSharp.Sandbox46 /// public static void Main(string[] args) { - RunJpegColorProfilingTests(); + // RunJpegColorProfilingTests(); // RunDecodeJpegProfilingTests(); // RunToVector4ProfilingTest(); - // RunResizeProfilingTest(); + RunResizeProfilingTest(); Console.ReadLine(); } @@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp.Sandbox46 private static void RunResizeProfilingTest() { var test = new ResizeProfilingBenchmarks(new ConsoleOutput()); - test.ResizeBicubic(2000, 2000); + test.ResizeBicubic(4000, 4000); } private static void RunToVector4ProfilingTest() From 606ab1f9939ecff0921b122f3b7ef1187694418a Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 20 Apr 2019 01:21:15 +0200 Subject: [PATCH 082/223] add ResizeWorker.pptx --- .../Transforms/Resize/The ResizeWorker.pptx | Bin 0 -> 38674 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/ImageSharp/Processing/Processors/Transforms/Resize/The ResizeWorker.pptx diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/The ResizeWorker.pptx b/src/ImageSharp/Processing/Processors/Transforms/Resize/The ResizeWorker.pptx new file mode 100644 index 0000000000000000000000000000000000000000..063f72de84022037dcd5d44331b3fce6b12efe3c GIT binary patch literal 38674 zcmeFYRdAfkvL)PNW?7QOOcpaUGg!>btQIpfGlL~r%*?WwnaN^iu=v;B=ll_K=kAE} zFmH3`d+6?ptbVBaYGtlmnWZ2N296GZ1V94-03twCm~^SQ8e2gHq-JKtJdIzvqAO4veRc*!D3Xhg>Ecr#Wtzn$qkng*?sIT=j#`o^wrFwTEV`@0ulKoK+(?Ma(o6 zV1?^m0ho7c+Dt1*Ld~jZ?+C6|LLJq|+i&iIw=*ZuBY^E}n1xGL`dx7_N`N^R4f@qc zV;47W6FFg(7=5LUS8=2!NVbTP@4jA2c0#9ipx=%bW-C=6@`j3QJGk!hn=;AiZvUf6iQY{ z6mxPq+-})w_V(mmv_t)qDgZTeoT>mdiuSi&v0%QDyIK&pwxHtP6HSw&q zoybx4@H4=OsU2qnSE6{ft22Mwz<@TOb9|hiV2f14?!gxQGN}$hx3{iLFm9<=ee0W> zEdfR|AyXkc2c$eKVv@m}wr)kPxd@%Gdktfx(6vTy5ANlUaSy`KR@iv2V`n1HJx7_& zsGnOk6s={aIO#r2dFc!TiU{CKJpV!;|nYX?XiI%j&~GeP8R ze!`|^8wOb++pLEz95&hwtbx#-UCt5SCk_~msxGdHrXxuW5<+PueRC--syS%44Ib@v z&_Y64vPM?QJn;Y(2cIS{F>$de>^g7Ay<;QW8!Bkcb1Ty1R)EaHDMu;0u8z?aV% zf1N8QYYSr&r+<1EwCG6{xM<(Z&m2bxtk_KQ! z>jATH zwGLOkOtEmULZxQ~U0dcc?{)n(8*f|2T2XgfEri+-n%S$>2lxHuO*z}}bLz^(n)S7T z^oF%T(|k@0f{Y#OiTHx6trLec?qms%lar6?0Z2N$w|lqckZ$(cj@ylRg8qbzkl@Y* zAUFfm<|il&`K*4tNZJ*DC(Dj_`UJQ1hgmdMKf9@ghH;bb!tH~sDt+iNzTz%9^b81d zSBY;LhoO=_LJ~2&f+TTXGNHgPXen$Q|HQqiUg)Z+-A0@NXRKR>Kb5yYql!vqQSc!3 zZCga-0`NZ1`jBu#&@msjp8?R49%gd0g@aYAuI`=iuM^k(T$!$ea`f8tFJ(r_Rm$FK z>y4dsPI|Q!eEUvjh+EneD;i9(QaIW__OD4Nn4lb1F==NtHoHyjkIwH>L>}jGs0jlAtuyV&aGffbvl5UFz2#sR>HoHL8i0<-Ie zKUzHJl&^T6tJ&}O20P)hJJ(=j_=l+E@qK=Y{WH-bpysI9OArI-Mg9G+_F+Tdp@u;V z-!66CU1MDx6J2)%+%YrN$qh*H_R}IQ3^A9@MTr=QiqKp8pu5m78BhXdb-2UTHal-@ z;kjIyk$YJoBI`wQ%ri~h^AMAQ5ZQ_Sm+5^%0N8cV61{|eqYSv%qa2D;5<)Rw3%>Jp z{81Dal%IVIl(V~=JdodlY-?Tun>$rUPB`~%ZqS>08R@PepWsq9mNh||^u zt6*0vyqBaU&9NZx{4=2~8QvV7m=G(rs5lKPkf67;N2@e&zi@>N{2=O48aG*?z*8Ex zVEh{C@s3y<pixok!#%)dv8914gM4F(ccEiAt5LyJJkv} z(3I?`!wS-Q;LJ4r^9&HV%-NC9L`fHiACKX^Qf9Ymm%iqkAEn=karnBCypimg6Z_Sr zEdY@$j(oYyOKtEVGq@HR`Xk9+D7mpMB3?2SloAlFLzIWN@mEPNCC{()qO9fgK+TDF zrPy2@m0b;?Jv;WEwn^692DWpdkLlHCrm&MTNAe0AKSb6?TxL8nbQnK$hswDifGh-L z1O+5ljfer(W%y<<8%|B9NxgT>!%q?LOR1m@Uh@sWxn){l~f zF1sy;mX`BVB8aMmqAmUDV&IaB32tNPui>O>B~$O$2L=`c_q|-7u;kdg9Y8ty=pHm0N;67(!AIY zQlumfQkZo8007Y&6sbb!J0&*=I^3W-ZfLmAI25UTY?5{$c)5>j6vM7M5Vz%bC~+3- zVj{Rx|I5bhBMxPy&tpu_P{^Zj@!WjQzRgupT4N+{VDb5A4(k3cu+KSqLwF)3gh&xAK44?5E8Ad{6!Wp>8`bUy<}z>TluZg0|GtmAhx^s$ z53sSw=5TbDTRegYi7TeY7cJR z>m^ghs@qL5;m$7mY8LWB#Ip)8ZVDlI0`zDFGjI}^AtdEI1*|OuQx|~;|M5W|oQXM6 z{8t1Ru?{(gn0HhH>JttY1&fOs@+hPPVR)elvM^CYtQm5D7|(nejwCM*GhziQPM_qa z$q0$zFRv6F$ zT3H#mab4O!;maWg?dl4K(x{3nt^^kSbM0kdv=RlzjL;xxR$`2Sco3~t&^a;eV&6x^ z`ebaK4LI{=1JUarxlydeP^P^M-`?WB3bg5{dSZLUA^TRz{2CfL$x}sZR{zs;!7brY z#jfm6K6mTKKPGp0Rz{##kO06c{XY{5roRb==7#N>7)F<#{+9pro5<}4$voAcOuD!Y zaF>9BRCrMh5vl~N*_e%jp02?z-P|9{!b)|t8V=fP3&m|Hp;=C*lUcXkq0FJr7cb>1 z$2tdXv?nLV0lK~6scIU$^kuh+BST0qacI@&vz9q2z~o$+s#}+vw^QHu-FDN(6sIY3 zwR0NYbei_6blDP{rg1fkmonR%(K}kpPAP7u@~IN1Y#+A5s!^(tIC(V-MOjGiH{>^u zTFN?}vie9BUEmub7yaC%`X*guF`l>;K9gqQ?IQecgI)UFY6T7Vx5I~3HUj6IHC5n~ z7`xeo=Xu+Q)03{}!1^Dn9hKwJ7852sE=WdKEuFT(yO`&vWf?OyxXwDYdbZ0GW5X<8 z8{d)#4W9*fjSq%3o{5x8mtoT}x6nK1EPW3nrS`JTmh=5-2hI|AQ$ru!1N?OOrVg9- znKC$?EEykJRNNYld_qQA0RsU+WMv3c8RIBXRKq^s`qi*0rfhEc535g7(Ol3#&7{+O zU*MU%P)@W?VkzT?jK^R^`^Fk0L=J)IrxXN2EeQ=u1`VwbO%`qVNh+FqVFnMbPgEK4 z7I|oV%sSX6SHDuxP%@@DFbagBLSKp?pbdbhc(eS?DZY^(SIpQvl2@zc*ks8xT(>&8 zcvw`NiHf6`eYcBfby+g8g%~?w(Zh0GKF%t>HO5a_ZI>BKl*}-J+-bPfEWYjV9pBbJ zzY7UzcLLKq7FA3+t)1zreKCM^KlB^uqcN_8@2==rv}ayg5OY`g-nhvsu#)d^pxfoD zaG7Shr~`5Tx_aWxqy}cYk!+yDKL3r9Tt1tzEEraD`Fs(y+<&HHRfYe6w@rUUc^!k)LN0MRdoH@K#PB z2sEP~m9p2H(9I7FpA-h6AB^*{WOk4CMluXK)pIyh29Css!h4f@kUV*leN?SPr72t0 zQwd);Ot=6Q6#_;9JRRBSi(gMHL>_cJ)>s+@k#}jsGi$uJwI*wN5nZIa;Z17$%wg`L zq?iEc8?c~*N>z2!%qY@0p!Guh@F4$G!;$%&xz+vqJOAW_C-cpl@s~B@eJCl_NirFs zU=WJ%d`cJy=r95tG<4;p1dg3hVO4Stbdrjkpz;@c&mX7kZOLu9EsmKjI|AW21MxDi z3fPl%(9_VO2UkKwVct?yOxZ+UR6B;m;6*Qp^zwr1Bw*cNAY(i?-?aNr(I!Ho{wTz< z#u4F%34r4FheUVv_sBDV=SL-Q3?d6p=EO&XGJ!(yrl7_Y1(i^e#jQ1huZP|Of^GgUW^g<#~LbR#*mkmKT6@LtbY8Ks_(0~WP zK!YU!uu1*X@uc!l_s}8=kx@R@s3~w=xwdRA0GE_|zlAIJ98&&(DqH`=Trspk2apLt z+vAo&lWQy984^gm2ki9J#yvKhecS3)b;+|f0aav%g9I6ZRme#5Mq#^@%~ixOA}1WY zQb*xny3|i|tw)UejPvQF4HNA$+Gy7ma~2L=XB{8D zMU*zFyvT>etWlQj-959sGT7H0nE{^a4di`BlpNIj0RbBe_^_*h*t8GSKiAKJvxs-Q z!fy@WC{e-&W@?h=uCaZK{geztH~mP&rj%FHk27;UxsDc7X5xPcuwE~>d`nRtyB6;c z(_K$$=#jo$#S5Z}z#s=b`x%Z*oJp+4G7lr6pi1zW6$PO5LtN&|5emty`*Rt}DEWsF zGbopB<)5$kX}EGP@t@}(UvoG56HL`9zm&V(>~OO};dgG9(9-uH5!fxo%xOGwc|E>U zSbdLGF1gjt)j7}4JA4W)U>2_sej6nribeKEAw+?P@BIn_iwc2UghfK+{p+Keu;rJ~ zZPTQWZD*NJAr9PB&Y4v>AK0>0{axGX;GduMeAs)>lTdh59Ab-8E;wPqI~Ptv(d&S( zhU@rS){fiXCA;oFw&LquQ7p6|*S>uelw*n>YGCD>SfsBtpN8b%{I*HrgHJFc4@_%u z+hBT>yKVYI_EEdYfB5|P@m_t;r-OBJOd(Ry9_71<|LaH-3@cE;0e2Jt9!5?+_N~45 zH#}*S0yg9-NGzR`L_lyHTLmw+$GA;YqX71M zNKW$emc{MGtoy+D!akRCEH>Pot}}^Ys_otTKjz%h_q=DFpBDDd0802@bMAjhI)5kN ze`nr*i#btKov@#JH`JBR6<*&t`GTNGMq3ymM>t@(sU#OV#){}u*w0zJ zx>Rhu(Zft4HzuZpizPmm^KG*8jToll zz1~ss-Rx87ID*aSXo_R%AgQ`v>WiXjQ1UV~CpK;P{tuS^H%IutZRyoSEU*lpig)>E zfeZcLZ~cGQ?#@zw#}qIi4|VG~UQXYg4u~RyM7NA=4D6xOGnT&qbg+`GseV-ET+D4X zd>5ckJp;Rnb}@ZtzCHcXv-70n=m=RFA@yV47h4&Wxco*4SLwM;D;iZQ$&}cv12t~; z8|8Dy>$8dClobqYkSVq>%qTRg85OUk&J?e_i8y7tN0}wE`I=gtF?Qf;8TKU zuN$sA**9?D^$qj&f{&i@RsESUi~BGH`L9;97{bP=MuM5p>dj9ibg;K$snmqRgQ?8gEyG;h0$ zNO`X7uK9;RW6TQfE3;)xd>a>8izFGe`kH`(*}SZ^3H?q}AFL)_&a;*9Q#|cERXt7` z%<(fDwprdzYtrB2QSf0ir;Dm{uD{JLmX4H}TIHyU^=-?Eoe`{0c01v}b9*yzD<;QWU#>gx0>o{gX!QY;D(# z^r_`Bv;9?2{a>^^Spz3$6UTomdjA^yOV`tUv;EZdKDv841M^<$r!~<_>5a1c5-MH% zopD%>!KP91%HzqB3pR`+!5%a&XfN>1a`!@#%*Q0pWIb?tVBon@oCR-bC)(*b@J|o!btPYipf0M!K zZ2@5#HI)!Q5DN3)z+yQLptZQID${^*x{aVO_0#g<9p-pLGp3L9H(J}&m%N=l%Q=*H z@5-3EO_D8hZZs}354KlT0C{GQ?67TUc&3`QoWw>h%-K{thF?t8#Ypd%tXjL&TUj5j z*Q!adA=%CoCQBcHU|(#&O0TyvMx5M4;ySP;C9pmDHkpQ-d`jT%t)XXs9};^ChsY%O z!y?HU<{=q$USz1)cB~N_{({%1GSu0P`%u~);ub59D9??e8!BPYHaNbi-&tA~vzw(`khka)*ccMZ9w3! z&^hvs5Tb}KJLn)?ZcBx-+V#g)Xkt4uFmXx;gGN*t1k;wADsnYu%nLi*JxPJ9MZfz!p?w~^XQD_3|C}v9CGFuchj8`qf zI>jBmsy13$fRegXrEj^>_^M=#2>a!GF_Wrhw@#BOikU-v346#-_>}#=ql7dj-aW23 zVv}ZQX+Tm>uS==redrs3-K(DAw5#u-#0Ej4g~Y3*eycgQ7HJmYH<^TxvX9PbQnO@~Xf$4e&~qFz3v5|O0>=J?@3FLsIo zcs^rf(&bFduuY}|Xrx?vscwzKPje7u#;)r6N|i0@JQ~FA0sUS+0^7GYFf&iaLIbv( z1L7I-Q8oMWL*b&xH54u4mGtfr%sU^yYrxH3S z@sC3WY}QV~JJ4wdLKmeT6LAh6Y`>Z1ZT$$zCO&T|$%`0tk8hmB7?Nah2{RHzh%~-D z&;r3Xf^%mK*I^F_1+S{|1{IRnlTO)N`ZnpK(WtPFWLT&X(W2k5{zK70tY@sKFm%l= zXN(rKMJQcIL9WRA!X%&@?Yt`H;YXP<-^CC&=d4>S>J|wbEKR09&L&?>rGpR;+!LA* zAtflc5Iw=AlfCJFzHbF3)Etm;i>-Ys+}&_jpb_>Z0G|mPX47%{wD{#u-}@S5W=Y^V zs%TNOsxf&T?)P1wDbIY`p&jek9id z32*hR$1ooj(5`D9-)h`O%7!U@v>pW$wH?Ww&o7FAc&RDt*lh=ezTXA;(?}v*27c*g z8T@wh#Q*Vn^VGeS*HsqD=RBudEv9oOrg+5ZfUJQ+jLe%j9U8FTs=8w+v_pwCp07;TjT!G#*gTOK{lZ1e@h+aV@q8wJ-lCfhMQ9>2RgDI-J z>BqP?z;d8+tt>rV_#l8**U#BmuDT<X+Y{v#42WZX5iXsYDiThswSRCvtx^u z3G7j0TS_?@t=u!-bd99RB8StVQRA1X-CDs%mPDQGz2>eiTkDw;nQ{+>8a_+Ujb)xl zdFo0&h{)VF%vq^%lf6Gv$Z?UB3Xbb6t1s?*q^N&JX`M#7%L$%lq^N*3HyMdfyCJun zAc@w(o9GUkf=26}Zf4=A~+Qvh~m{p>VAnv4>_5Afi#C-!- z1z|I|R?HTuCpAE;r%YJ4I_RH}FzGbz{%Z_}iw(D~e`5x*aqGLCd`%&|1 zE}^eyoOBlVa3Jko8prGr#qbvKU*?+{D0gJHl85tXAvy=!55YFx)Y8yob%rj^B7X`x z&7mXJ$18iUu{zHmx5RQctJbf7WiAvx#x_Q`)mgiP-uk1-}joyk<-SV@#DAUIs)`}*oRdQFRK5A@L zE~yYDR4ua=sE~xA1FM|he-)pEey!|A(xe~AGdMdrr79iMRV$}ffm^CIa4ez7^Q`zr zuL?YFa4)3TOouj8Q7fv64SCN-?~7X9sIJqyFPW`!tLILZQ^5|hC~8l`moa!~+F0q` z8N9i5Pb1K&FfpoXLzx_yUGUP$m}eK#4m>A;hQfDlrV}H9(uvuK7=YPkwe0$ z9|oyVUKz}J@^(SBHti%EHa8sWIkD;w1BXpXI8#SVhmL5JafVfQ`rda+i#3+un5lF9 zts`!Y5OkC&oTotKW7XfMQK4~BAR&?)in;F1e{gGGNPxZVH?tLdb7~Vtn;4mL-QH#- zro2Cs+bV+bE-jZc*i)3%SITk{xvq15sydt8<$05exz1Qesz|Da@$`-(H#|u4!f$9<2LCr@ogk z1o7RuLlR?C_~|2oPendUv7paZ&?ET)x@d%Dm%FTZ{ zxi2TSadbC4udtLpKXGx>X)#-4f7TiY#Wi2v^HBcf687*j=y`bWMd8S*Iirl;|G-Nq+5C%W0vpE*VzONW+83a}aHue{r9~#@55ed4wkvnb^NLI}o zn04;;jVyjz2?jmCM$ka;|p#*!|}HIi&EciMrR zSw(j*_9s09m@y3?CjynkRuk*0_%o2A#VI)KP{}PWQ$?OCkhb2MzxJm;Dfdg0x@O`rNzs z%AHMm)&Msg%;&?)Ll@yF04VmWhe(X_-t9s(FS;0gc^qO(Z3EUHz3--7AAJLFhYXCd)!b7X$J48wf{&&W9IafCdcUl6{>Z6#V1Mfw)+7v67t?8`@OD znjy1BKG9GpwDkyp(FX`g@cPYC8UzI|!~IE~fe)EgHFMSTeY$zN)a&wkKZIEy&^^$5 zaauYzpN)C%98tiv?;#oiND2{%d@Up_Op8l{QjAhA%*IbxTb)UKoRiynqcYx7dDuce zlD%2kZ{%ar$8}56mmtp&i$1Go?XQ-3d)6FVa{q8K6P;bQ`?D-}FP3I>#MeL1_wnZR z`dR1rhkl)?@0{5(pWage=|6kVzvYf`wNu+HG4yx6(o4UiOGXBGFm(-a?Q$tOYhX51 z55s8@NC;wmCa^%lz4F1~hLo4tkG*!oSh@1shXGv{XY(a@=ef#&G@ zd|im+#Hu03Z{E}GjflvmxEgeJRk8@zymlKJ>$Q2y$TsGZ+nu;uuf{UzlpDvnI=L7r za4T4tccjxc0#lneqE(MxNQFXq-}8iyb0STjw@?dnqT1jhqa4BY{kqbqDszL~vJ?bn zLT<4>ZkUXQo@qUp!TE}{cj{*oU#h#-r8Q}T`Q%yVZ1&3I0n*~s4ORIH&mp@53x_tX zaMkEIc%x!jlOnajgNf@eRz_PQK+XQ2Z4 zrV3;FGIp{-bN#WNopihKqGG@lV3$yuxx-OpSRYQqm6Z0)6L9oSM)=0i;P&Va7Alz> z5eheH&_%ljFNKnvjgEY51$Df>VIx zy=$->NQHRxF%C?|Nbpwqe!)yj&F)~h70z&DvqOX` zY=>Zvo*zaocLE7=Kl_p2tcpta?UE>=K!a-Ndwz6oL*e&T*sd3@Rnn1NXtb!hxc)G= z)>u$>$9Q411UFrLD8xMv7g&ZQ;MmQxDarDH*aixI-7uDBI!ndT#ff?jibVaWa|;D} zOP;}H*_aS|H~)_v-~3GWIod;Q)*yNq@f<5mic)#utb9r-Cy}2BLAMr&lRt6}gJH-8 zZ|;J$BpI15reui-fy~D-%bT%qtl9axY`}r2rObzC{OwI<2%g(HhH&DRsuf1jpg+^v z#7Ow*nZJ{s(LMK$K|+1;hgS;IPN$aRabyvfl<4Jo_WpE5OKgKvI`iQCrjeq`&$oiQ z)+zjo{sYg=)!g^#^|)G(&-eKSdP5ZHR(>iA$eY=MHbgWaiCnl$q)=sXR7hEZKgydL zM~Ya`KY{M*5z4cNXpKLZozrbpaM<|H=zJE=wsk*UgLK;C_HT;9+txydytM3D@l9&IE} ziUX}Z<3Gsj`kJEh*Za_$ridQBG`^@NE-=)hx4JYp77Xwj4Xb#~@KX5}4hslMcdBhfz7&CE+tm7I)}_~gT; z*WEG1rbqFpK2;213Ke-$0coFGvMYDoL9IeYqeC3HTcCzk5KVLV@WKLYP5psh#2_^P z8~r*cgc1lNy-padv*SCxxy(OS#L)4oEA!m80x&+eV9bbfi-(pQcGMop=DVgP3nyZs z3$^Kj0ha!}S}86|rBgU#>t+u5LcJx;NRs9b=E`B;=U204Z8Y>N=Q4_=ZQKbAb)C#9 zY$LnWY7>Kf^$kJWzrSK+iT13UU3j`oO&X{S!@Q~L7W6%Afk1!#gSFY9NRpZxjF->; z>@M~?ar?W;vj7T$M$AdW7~o(#rlm;&%Xp=?S~042QkiPHrQzHYuII(A&j z!XtFD2h9(@rwoPKLyj~&+IjlYxiV&V1H{@os%?QK;oU}e5IfpkAfaw5*RS2K%28t4 z^3iqkJ`rt6t4x&|+3HXundE3}Q&2nl=j?0y=2+%T)wWLd@#zhcBHis1Y+ML6`WJP3 z)US7(u zcrYwC_HAvJ9Y_*nfMGF=yx%WwfJ{z_Fo#}3 z=LGE&Bv+)+G&9dtuBtr56t)*h8~N1tY^i5p8zrWC6>nA-y_+OX2UJ8=iusue85SQ# zcqUwU;7(F#r@gJnR?@ffgf3!FX+Hko1^~OC_RXwN(PuN3JE>g0Evh%}i=FG( z@$(1w_?e}0a83*xnMdm--F#CYpN{pvcWWQ{9~6{-vOin2|Lk&E{%(j-Uvtc2!}_<& zg@q1Jq;P=*IdL8g=hZm`VKDDx@%uSlb#BU(xMWE{A+IFq$IjQUTjDZ=*{T6U^j32D z(tujMrQHzSXkhr$z1i-UT9r+0)t<^m_W9bSJY|@*Rh+2W7lg~@F7FY@mx^Uw`Q-%s`nN-L_&7kWlGo0T>tO$Ix#pV4~LE`XVOqTNTa!5Y`LPBay5vlzw6 zP0XEX#py$~MqHE8`({qf%kAnBs?BAnivwMifcE+B7IR(I3r%3*cj(E_tm;e4o6+15Y7)a*jxrzlx~ zy<-wJaFj4Kkz_C4y#b9N;n#{$JS4s32D`V97_&_Z&M4Z2WPKjX;v`c12LXH|&>DH+ zJXk*fyi(RPADPr%?g5Ti(OtV^rX7oMF5&bo1>NdV|MrUQVTcwGQGm72Q4-kc0qaNL1x`;T@;v6| z+DlW34ZgL~1yOIvv_Z*v#}aMK3E@pD&}Xz6F17#G)U|1nlo2n$h^lP2%eMyXMLNQyB zDz*$k;nmV3myN%2+?Q1P+z>13B}MgpE6;@ zs~ckXB`*Gm-Ifk|Yok;qTlwf4jF*voMMG3M>9?r|3IcgjWVG`00;cerGMPt;R10o* z*Q;B3yVFiKdBnmo%M}gSLhHwZw~>O=lg{IYXiN)~M!PaD+J$=e1$4y{+st~4S&>87 z_TiG@rj>@UI!C5)JC}65i9?MnNRn;P z9;yWFrhTCc4n`*7kXYn$#lHd#(pqWNiWxf@->j|0JTY&Jr_~y976;x%;$2F8OWNo= zk1AR&qn0|acg)9@-B4`5^5S8p+0vACop8w6ZWX*1y}ZUT*fQFoqe; zrxl>X=~|yEMuV}`eD2rqy-G$*qvBdovjSU6hgo-M|X%NNN-NsV5N>~|Q5(du%VnCnYIGQxbf!TqXF0kxx zt`@DK_?4bIc}L!JSkbn)b2LB0^oydCIg_F)IrX>GVi0v71@YMQ!lCw-0_fzRac`&Ds;eS~0kuT}NAkZRH1K z)Bn0JUm#<2e;%F2@Kv3@08$%o32ADW%klG8Q*KuLNLu_`*;!KIQUa4tz9grv2NiPFoT^zbD3SWHN0JVrzYpsxm4nP?h2r+&n^Olm`X`yobUvD!hJQ+Cni4&#>LD1|JDr z=HYnGxr?ew@DQ$9J*)Di)sx1iXWs?Yv#!;MuK6jdEDum+cw+z=AwG=X?lWB+jR21T zR%_MmdgH=i!X+X@_l?pA(TpF+9vV18M3 z7?d`FlfF^NTTgtx@ZRl`n>28cwxD%?G%*8FD859=F&kk36KE_i<|&Vcxn@wf}cV z!kS1BW4>#uzmUj?*0tFQv?b}#Kvc0?UR>FQ3T2MUZ*~jSIIe+#+Va^_geyi#vJ|x0B}asVU&D$ zU%%}Go%+h0(5*kJ&&V_0dS%)1CmK0qrS}FJS(}8~9pC$OQn~0H{oznRf>uyl(j@em zhw^QI$|Sx%!Pwo3oUf=(c3(3dD29tc=8w|xLBu)XAcH{I6E=dtZup@b=xe#Dg%>X* zrHr=e+>R~Lf0jV+$GW$O+SA7VQ0%vy7PFcU-QtFL{RTmzo|M?O-pHvFAzf}rV9ouy_WFPuz zZs-$c62*dd3=OCPQVfW!YPqPz-RZ(&lxrk#;88Tg7$B>SgzfaIH;rgzFc(XSs^=LacMwzZ6p! z^=Mh(WbB)+#&#i&nwQOG61Hj#a|-v&&9y9l7~(@yr*&&UQHKllNVnf*lLOBZLUzhh zHt*V4$@1fxjZQ=d{bS|P{GLce$hR=x^@U$8Za6tRHgSgF?1Rr+M5Vicl`>7%odT;ts{7;^iX~{mx&HG0FaY2r*Vx|JD@|bNbm$nqLL_)V$HB1 zD5gO2J+l7~@ zE_(7&6VW>Ht(#vJpk>X@D`JIC9_yR=zjpgSVs1NC1p!yCl+K=u1C%?|MnUM2zLL9# zz*bA#A9G4MMJQ^wloEvGoYvm$b1G0BmHG;icdVTBY=iC`pYXy_G)5=aLY zC8FgMf`fUg={Sz~`VLQh2(#BsN8fiA-{|x`RVUkb!FiM2DdIzN5~K4upn``f_0^2M zc1YBxQmm_vggf}2wFb@7{?M|{^>?0v1J($1TAWC!6MWv2^7KnogLR9;bP#ZAvF*P{ zx!wUK#1)?PkIWl6Ac8?_ z0l#aSiNc;mEbz4SeY$?D*7NcDc=_5E`88!|MG459*(z*->Y*WnG7vW_TP{}X# zjkXSvREh?+rFKRZQ9xR|8d>r?)y3c%p+MCFJ_W^b9P0VJ3kd(RfUbxo3v-rTSL~0? zj?Q!L!{Pn;2!mZnv#!&td<N1(3X5hPGewu|JY%F z8o98{`}~6cSryw7!P|@9&8? zd}_1A0e0G}fQ4j}HTam|#ETQtJ?g-%E4lk};YOC-G*c@bbxKQs!G6nyJJIin>|<17 z?cJW(iJuQKc|HhHEcbkDfzz!b$BT37Y1P^7)z`eKChz9y0&>|d%39XiGGs3VS7IE& zt%g_rr0a~38e^M4b!@2A)DmdTeqko{i@1tX4BZs^1wu?Tm|5f(Wo6$NGJ_N~`tiHbBTov== z8CWBw_z96WJ z=gtN_!VJv%*%Ny?exigYO~@0H^d1O>jCDZv(n>DidPmfAEtmy%L(nual0#S-JSOyz zbMP!>tU?^J%uX7H1)#G@6eP6loYTe*F+mSMDPI9}AnL0)$}dd3H3YgJPa_E^#%2{^n`7xMc7(p%{puLha36b zJ_2rcfvCD)zT_*)p->qZoDE?W)e;mkO^#7V6#f_=TcW_FGd)^K%nGfxpPsG@m<#Tw zRIe1+w}>mC3G+r*bx+?WjeKor;Wj6iJq9fBK6*Vp+<%rrz26QY z=&Sk^$@o95|0%9>{EcDt&%0r4Se*nluE-r$i#xd@5VEk+DIp*hIOE4)l&E-Cb6cb< zKvnc3E1TFkiWPAw8Qh_0yyjjZK68|NUs6%y$b$z;?0 z`{ScuCPDqfmhCMvyB*Z~$46pKh3tPt_BK9?+dH1F(v)lnToX^!+!zcUwcMy7o4PpB z^{z5YjXt~EGO8WKF1DrJhH!pR*ZA0Q8}41=$F)0C76bw5 z2I-cTM(J*mmhO&yx1RUBc=$Z$^WNX@{&Da7O<1yUulX=@jIri?#+a+6Y-&x5zA_j~ zh$qwYI!(3;Y(-u+NcqD34)j&@32)V@_@a($SxD8{XEwhnprfP*qX?rYhRpB_d-d|7 zqtHBC76Z#1G@iiap_C-8VQ{dK8s&1A<_C6vgXQ(HuHEI6XjcJ1`c{$y2 z>rBnPDLpT=wOuTn4uMo;lqZJh0B zsR(v9VG~fi7Sj-&LXRS9DbeU1-8zZSZV+X+VtG4JD?-@-RGWyjbDcPVI18A`FW-N9 z5KaOz9L{tR08-B1Z_%&Ip#iydNOKTQe8)9z^D39S7{@1%U*?UV4Da>&>^7Q|@p~mC zJ%7(!V^<%02^L`>u(C#Cj!2nv4V!H?^AUUbrI-^ypWQp)z*O?HRh={WfKkLKCQkM= zMO-YkFy1wulUs5&tRp_zGhb{<;gC>$GCiiz>m7HRLto8Rt#RT4a(?S*#ydSP^isMt}s#@OPX)EGYmksh4% z48Pmi+Isr)fiIGRitGwwdyn52WH^#>etXEJNh?NVR1!QW9zPrNKE?q0vBq_j0FCR< zf(6rvm}?aSJ;>bieDZNX%V0P4TDvzTV;|m}SH?FFTR^D`z8RXWV7+F$X7j@XZm0+`#HW2BvYpKJFFudqLIy z;v@JVskV-)ISbWrc^@3M;Z&bki?)+`yQR6BsD+JM&zt~VRumag66b$Jex6W)^8$}#YM+w34}-yIP)Aljp!q%$uqoFb+}u9I5d z7b%(^A>P@npTG?U2kH2QmS>!dmf@ARjs?=0TG!TYFNPm?gxesI!hTHS2+fkQ*`3Wm za-Hq1SFViww_UzrnHpvzq-Otu`ecEYp*xUS_FwlsVz9M4eL=z=?l*)z*B`=O!)lHb z{gzPYjL4)#EgYR(Ug*uUaeYR?S?D(oVDHI#o}J=^I;B#>IcTu-ZAz`j#@_U1J1Q8z zCJfJGh~8V{;w8I1nrNVjRQhr%s*wKfBG3@WHI-dWPCNbGeyLukOm>1~D!HM;M*h0Y zm*lcRuiG_4_tAtISGzIVTrez0@7R=g@YHi}W{PDoUE@y}-m#Z0)wzx-g$9%@oYHQ` zM*C>Rv*@Yq6-$N9;`KNOM_8{PlMHlFXeqnW!MvttK!d$J56{}AtUg1ouI+R88Oc6Z zh~=PDoWt0hm}PZPt@A^)NE2GyG)%&Flo#p|jrnYtfJ+Ep&*+zQsQ>% zKJ}^=GnL@V$Z-T)G%rK!kv;nzW6);VsT7A%U68iyz^Vb?KFw(WHq&H21+sHCzwvtp zZD~?`?lDX21{Zf(i-wTB&@>r_#N8%EB{nkWIy~lhd!=4xj&YGq-a&UR!|I(dg<}Og zv?UQHQKo9-8c0!+EP}Am0n+wmpXohgHk?LE(*$#?yIzV&-E+h;r5Gof#s0PM>OwK! z=iCrdO!(AFYF>I?5|6r2^pty%Ym%ayUc+_rVO;PoKW991O#$?}x^2-I={M^yp`m!X zf5{e~J|N1;cgz&F=b@U(EU7|kM;;+fl!h(bXg_OM`<+G9O>!S zV_q$`(}FeJ=?^Cb`voc-pD@fc^N9YJ@lhoH> zz||Xl;0z%hoB&))@1w*4G;t`y3*e~jT2Ia%2?jEQSK$-UlJOm!UHb-%3)DI5Ps14@zv;C&#=Q{1~W<9WqZ`=iO zjig@GLg76IIK?c9;hLWcu5v_%Pk-r0f<*5*9*VtOYfZWX&1!=;Ey37-8i#H}6;2Z! zZvKI%NlQv*$8W!eV0sjPSaD)jU>21V*(Ui zT4eoBar6$Y`SY)C9Z;tw)4>{?nX(KY>UW^KZfl-%CpJhrEY|PbgnWyVW=k$;p$(_y z_(EHBUiZj3oq8$m4b@{_T$FV+K^Nq_NAB7T`sNU&E@$RUGhoE z7T^=}CO~Qu`I<(Ud}CVeDfQRu$z%2Lch(V?;-SlPu7OAmHqE!>U)P#Lzhcm`h&)mw z&*kQuwJ|EkPl#kiz?m^B3Tah5?Wo#$MY%f{k0F^`cP{8$Bb6xpja_7d|2Ek3476SI z3;At)gGg=$+P4k{u@*@{Py9O?)Gw$)OpHGSxXN7A96Pcn!>~8e&ih$X@H3^fwHfuVW`?q*1NQVb4T!3YM1oQ&>nhFT*{xuzBdO zDlOOZj{DgWzN}ixv8&i0pVA0tnx4e6K^4sg4Glo-4`3R%-W@e1pDNi7BJL{V_>7#e zuyb(8JF)a_9V3*f0gHECAciJO3X%deYz?3IHACz)RF+#_D-V3`#ybyJIIG)sOghdP zsAIEU1(3LOkzFiWe=bcQZcrHJZUc$#M(YMLBzzJ0(3qlTQxl#F|0g}X{=>(HN>~U* zy1GiA!*E{{tuQds+bd%~ZAq9d-dFasXES$d4Y!)nf@{8>*ptUH%p4X&AHyMxFuJOp z*$j0wzn)+Rw#33D~X@R#No0 zckXCY;1rfuEUhNoXinjs4YRXq8DhqBH5Gda%_6cBl0ij|Za9sgfM>wubcylh*;;C6kcmRb)@!XGFyV81gr!R+f?aQ3sOZYSvb)%D;<~+kr#d#LMcQE z0f=2`{>?sKSZDx#SrB;f?gLwv%6IRpWw(6eQAUeaV>W^VM|4P%*rld|lEOE|0^Y z#!%0Aa`|%3Ckq^}UGbHP;I>OSo7|n@@~GVMlkEzMWX8<8$SaLQ1vIZ?1P zPZ1A7(su#quD;tjp}WJK2;NOt=rwsGVW~aU}}SP zO#&fe%FX!V7Y@KZ;s%?7tfBBN@r*~n(IVufNJN@w1v49MVpe*{K*k>&e(&ijNJRC$ zGhUleJ_s+l%KA`5l3iw~X*;PoRl!U8+9=-C4IRX2G_?+srkGB5&b{EXJEo{Nv@F7D z-|!=qt(nVbt>#N7T8yS{x}p|^hk@xmGesVWYZwQ@`e{frQSI&1p}qbts>XI&i5hxx zXVVK_DI2y$s?1#t(6u*HN?nk2avnlp>JHhX*oFkRQ-#XCn|YBEq`SJ3F35R9BQ&_T zrrFv}P(NZI3AaFg_8JY6-LGsGS+1uFY$yrbO#n(5UMz9!Fo2GEQ8;M>6;*tR#7QAHb&wb&titn6^UW7+ zM{08K(j9i>_<8d9H8fE{?{^Maro+7>8lpGyT}b-muPXQS@Y3@x zSQSmq6Z$5ucP8LjMo}z*py~>tnY5U^l;?HW+h>n;O^7ohKgOP}uUJ+Kcp!Qn`A#~D z!S~7|!ft&oc)+~x^nuw%v~)XTLThL;%IzMYYsV~qefY1BqgYsgp1Jf8`QHN50A<)81u-g4hC z9-!*c!UkGWQR0SClkz;(`wUlE^&vN=q(pyo(!iY8%si#CL~m3%&H_`R_By^|=2*iE z)QJDJc~|6+8B#!szzF#_6v4l|e=My*ir|i42PibmUxiA&HBg`+5y$peB3Z}jTr2*Y zYQYz=k{gt#+PEL;PXeV|ovG((@2A(eq$MZ2q+Z@*o%ubpohI9QgH=qBd z2{7%CZ)rq*O;4E3!-~yt`8gO|ty{P*FY~7sip#>rb}AR7AS_sKX_J$2sT-3FPcMU} z)PV0ns$d_a3RqyHm)mJX&R{L3;zcdysL2UFN)k5NA5r0-Q1 z>wwX9O~J&Y8KyIPG{Gjt*0<^qlo1}P{bef012g!Qmqxb1)ohkJo^^#`W;T0HoCa_k zMAS5~yO3A^oi0me2xd;izp;D?M3mA4DwoqB_41g4ZPU{o2 z!*XZ%orZqVxjOSp8td&CHndBC@pJK%_{MaFAz2etMs#KlLDs-X|M9Mnm)#`YdJIm} z2~22Tk6E{n4k^-U*kJ{-s3b5NU#^$8Q_#q`Nl$=i!YKgl1MFJiH2elt#frA zCK z2OvF2Y1q&xdh`^L(#8hUm9pn@e#tm4Z>wH_tLK^};cM5cZ|V8E@;Uj%TB5ZtPYvrGbu|wzC{~{~Bb^K}M{-ip7a&P=qJdzUs5LQQ&wl>idu&C8p=sB|0tZ#G4BYyrXp0{_JRo*h$ zF7kSqO+o6)M7_y6srzj6r9BfEkj5t%Jzpg|lCjQq?M5_BIyIwQY+^*^c+IiwqIhzr zAF~qoA%9Ac#2gK%k1JF`8Nhd!-+n(^aAGOQ!_xK1OoCFw|3eGut$uK+eGpBj=$|1`tj z2J-uV*luzcZqn)m(f)^s?t|Gmm>60bGW_`WkLY4*1L3$F=&hJn0*~#TZ`f9%ktwJ3 zd6Eq{)GCwX@M>y#vsGD`W1Dc%z{$Bz<*9OV6P{V~#f=I=AT*yu!;)2R5{^(t?qbip zTQ^4o*J7BCT|ZCra@*hBELm^Qb!T#nV!{j{$FFlMUFPP*u3(aEk8@0=?JW43xtwG0twpUdm~H zW4D1d5(R%8G4qMIg;Fb+gm_7)r{r6-MvF^k6Gm30AUh?;czW)(B#def_0eO{1Mfkqc9+(1Rpgk50@Pn-E-(2U9YZ(~hYFjTKzehl*>tbB0_O(w9RR z7L8G}P$?*$+oMQ8wys)y#5KZhbaPn30)d)83d@%H4gNWeiKG2%%%LU8V zx09QtvcCGdHK&)T>uaxWPX+`8JT`A`=c3C9yv=*UdGFR3w3}~MhJf-62x;``5KgE&l_d&W%`ApQ)^Az#fyBdxXZ6gUmpDT{|!A?wey*0%x>L z{ki$Jpahs>>`uOl!p9^Q!}IxH?a-GYrN#O56(Z>LoF6gmG>D6(z_P~-;xC^dSG-$& zjfXuF2fO!z|C1=1V0cBXOn1goZSe?=b+)*V7FS?W0g%PTSaP`#)6=wrNgn4fyZ zj&$0@gW3WiWrGu$jTyV2O&eGp1PYzwoqr=?kI{@RM5qtP>Lb0jz!9NJQ{#F= z_F2CyggSuA?kuS~R%abi&hr353U0wOS*K&6Y;mrR2w?_7gfGc{?(N6^`Dl1WFs$tN5b zJ*c_?J79=vVCl_WV#{h7WMWMc$EStZ%n+Pgdge`|BjXae0Yx(Al(xd1-B)lmd*Z#1 zo=#w%xoFvRcw1NY2(It(^nR^->)UzPUCSjho=60rC=96-M7Q7UPUXcCZ!wqs;_%PgdD8rW6X} z_yIM;WA9>%u(M+|K6~Y%lnEBXlC)1!Se_`=4SOT%%k7Qnu$d=<^i*G-G+dyRpZ5r3 zjpNf*x0t;X9oBqd{PkIs!`#wyFCha57qCDnGO9Hc>jsI(crvkZ0PxsRr^1~xc=^KsIUTN6!;mhKZ z*)18x_6F)g2KTHVP+vEs){kf8Kd+Fl$x4PFnNaL32bh-X zzYZ3Eh$(%mG2*fvYbhsUjG~@Kb!m{+?&vN^u$dF^j=wjxTa07<%`FF7bKma?MegzPefyKrT8m{WFkUsI`x68{at7!sw+?&6H&wEjeRbz<9@_|B zO*Az_k*bQBA=_mRu{Z^bO9OY7Vt}r-19fP(bzF#1Q3&6ly|D~O^jXg-|Ia=yj>6%*5o7or|1E+ys z0mxEfl41ZbFff1)=nDXx0|)~kz`?(NgAyd@8|o1h6eJ`R91INfqsMTMAH&1J!y_P~ zA|oK8Ai~2VV<4lTp+9-@OXZ_z{5e z2=E#V00Bk<07n6XKmh}Ges|vkg94TIdx8Izz`#Legob$p3kMH+pavNL4h8`M4hiwS z)S#z5L7xL4QJ_$XnS`Lxu5VyyWNcz;X7AwW*+B-VCx_f#*eI6YfpO~DQo|#=*URhmR-}tt< zb$E1qa(Z@tae4K z3Iqy30C2026-EX4=j)g-iU4!;v`7F+^NU)}F%V$6IoYC1Mo7K)n%l-}6Vi|G8^l;a z8RV!VpZP~10GU_vHi^9Hio7RmWeihH!-7jLW3D&XaG}FGjFGc@^rN4|(~4_;kLOc& zq;1Z&MGK}XOrBAbOPN(^XneVy$^EaJY!ua>G}i zpcOWXt7gsm>C2quP6FfNhGG!)zSUb=d`>MDD=pmUXRa1x{au5A%!4N79_PeKdBT1PKL4w0Si=l$$EY9U?iNPtxj) zCtRzac_R{%Bdp44E%bnkez*)W&4~c3BSn#VfjB%~tsTaF9>R-jzq~80GDc6^#KN9l zB1@lY4{MTX&BliQj>^EZBDabs>(lX`LP{L84}V-%i(!)Agu8;KE6G8#Af)@@>^Lb z>EZKZ^)6-?FYVM`rs|4p8Xv|^RPNQosVG}@b^5~FOsjoYeS%byT1ZP8{v{v#nEjXw zg@mx0j569rAb_UqP7o78=vxKhCBqHc&bz0p*34tq!*_Z~Y!iV^hO2KEkdZlQzI*Y-{H zVy1sE%|x0n5CD%R4Fse)YoCM0LYepttqjAR>w@=U*rrt-7t3d;R8VJY; zyXy@8m~=BiM>b*>MwLhO_ky;6l9wZIdlz1N4&MKfPLdI>vp(OW4c{TFaj<+*yX+I zXHosT0-I=#0-b!aH9R}UcV0$7Kuhq2f_^iu?4Hq<_gc~^>wX>5$nCYRLn*kue6X_uPK9}6t5)i-(LTELL{xc83q1QLh2+;WJUpyZM0^kfLjX!yx zgtlz%3ACr*KcB?*?MVAv!E+1~d{@XB@QNgma7Ex^{rZ1l$|(in_4RlZthrA}Be+pRzR?*CuevI;jKHfKuVL8UGHRguk&_a# zs$gSrcp2kDZHYax?>5k+bRc;`9Cn1Tk#| zD~(;}5vh})Cn67=L4v@N&V>Q5gySvOD?wH2dO<-zdUATgUY(}r*rxiGSH5wP#^o7@ z%`2e~`AOD9G0qTv%t$ZI!M8!9kfCH2Hxh2tKo6-ur@XzA9?|ZDm?0J~^(-T2$u5UX z%_!~Y=pa;G?XxcZ7ye`wH=igEGvF2FL;>jk&Glr(yGHirjk)zTtSAtWwOVFS#(r^| zB~<1D-MPNs<^`Nv^U}Nw2?n?HKq&0!YmL2ScwAM9Yi!ioq zyvZPR*naDhma6yA!p7}M`i&^z&yQ!{8h!M3-(%R0yJqptlex>2J(oQy69S1aM_nL5 zz&ZVV)cX)U@?b;OkZ9qR&f)nRARy#lnUG2qc7`f${wh0iYe6cdJnNb|Li0A!RZK_9 zYOZ_7K4OJ4&$O0@wFpbzRX>aqg}o5ZC&RJZo8;*;&Pl8ZWJ^&n!4vS-^2`G=Z?-F|`OC_vnL{WeWNz&N$q`sldMiL2Y)DCd{9!e4EOr$HAzAjzo@lJcO=DRE}& z@BrP%FaZJEP2Q6&Dq$eyE*uGx&GtaRsM@As`hK-{XDiFc67Pxh^F<)w85^6h`CTzc z00043D1}8AVr7TqTSK$D2N_vmPf5NjT%`A`Kda;SSKc=+8s1anch8DduGo+Q34NQ} z!>Edo%0Z;|vh+lHb;pvBD7vyJ6uO)sp`s}eaJXa% z1ZXB)!c{ylC{D}>;RGPu1JUJ_|3R7j)uJ!OT_L;)jRpd=R_duYg+IhNmk;R-Q9vPuDmoIQjo))D(+AU{&nXv9L<+?P7V4D4Qi|cU=&#p;+ zEa~~D4gJS={P~j^-Voo8s1+cWBq`xt8MmjfykPL3=2ncbiHDw>s*EhFK@%+a3NWqO z4xbefPIITxx@aG-%4!hNNeY6gxgsFL=RlWn38bbj0-5no?Za z7{sb!&m2fX#(EBzP3-UB%ij4;d7m$}wnzVTUvKQ!qxdFnQ>N~A>P{@I2CImgp&8wz zXz}O2_Ln~`e&)q;=g|iQfO)T~)aBl6?~9MA-;WAK3>xL`c@#g=mqJ%?X|2N_Jg9ES z_drrg_4{xF|G#h{0cZYtzE)^zcxob?h6zcJxvIDgX|rwM>^MKNr1;INL7{-e zs{!(g;hm>){`!w@M~glL=gUOb52}92@?o_xdNtWr zv3WI5VDk|trmdYi*bOtHI;QD^a)h?IuGL>d8TQIFIvbuzv{NXeq55q`j2zg7~jGM4K-g8`yQdX|Z zahs2i(zGReS~!8Wqc!&UFb^u`rcgW!o$bhTgfSBx`Wh zw)ZU|yaU3sHV4I;uX52ZMtKn)@5+LD=Fc@AbO1B}baD!4jb~M|!8rxgcalH#os+4d z^S}EpLP5rQj`49b(zG|SgZ3_DawE2F7($2BrmkSqf>LOO(l_|56z*#aTwh)o!iYc%mizPY>VsKQMy_UBIka+^DrAsD?b4X`xu$Fm`7h`QXVpd=8 zob#u(gnl3;P>+6+U3&#dgE{K6N?PDPO<vyKh;4*O`PB3w%!i^BjS}^{}$~lvu^< zQdJ3?rgTH5#-!SB? zu2i|vWjW!}+6A8DsZJJTHZtP&78h-rlEtbo`s=;gR6z1{r8FZxL*>{j}c37sX5fyWubhNv_zTl+of~{$_PB|RB zYwcn#udS@BeG$6K_Hdp_vJG^$z5TNgOYzJPe2Hq`c2AAu<}K?V=V)BGkHwSuZs3P5 zzkwbPO>5(L??;qX{V~69EyPp;Leq`4+g5RMqQ3B!hSp24Qs9@Bwp zZ&@tqQ0FRx^VYU)M=5fLp`^FdxUjV;lP$JC&-qk5`;;OgB*FVyGyYkr-BD%YRb6LQ zufZo8a$-uJaszqIR|cB-n%IfBxosJMZLT-f)Kgq)q+4=A>)~}C3l|eZ1+Wrqv+aMtLZiH{7iFREGj~=H{ z@cT1DpHX|2=jVYZpUjUAJ|xDHAZAw(zPcQ7L7_C|lc>1gw;3{Z4XHopq4$&{1E0 z4KeP45D!mI^NR%3@_L2Y(jbSJ$e|W_)U0YL9AjOHbmC#@!%MVuR~6rA9B?66+@R`+ z3LHj^Bh*P|hjNPwdCu0iD8j9JrBn`xQ}o`N{*lFtyRR5s!gQV#RK0(a6S=H|22@e3 zZ%SjBHynDh1)Ek+C6hNr)=Wg{!l{EerK@n3SLo&DTzuDJ{-ZX|^ ze6yPb@Q+COE+{j3A|G!$7&yek@$_!gvob!D6(N=Z#95(vfLHSGCyj#wb8}7$5~NZJ z0o&19GYqI9#$Otk6KRws@X&CHe=!ZJ}()05Hd zXtPzZvd=DY70pZgkH6HV-T1hP%*VkTh)dQ4Ux-V32;W^_%M$hVWN~`zf6U>ioj18g z#!y->H4IoN;d_4MO3g<#e|x4}2g@%6Y%09WmY;o6fWP6H`Rb~19hb@tGnM{s)N9}F z6nvlm#qz%c%7TH@0q*URT%EViD?#$U_wgUf?9Wm3$4?DmYbyssD+g^wR~tint?$$9 zrSx9`OfDQUL7N7EWzf_F#n}2OZG%jmKRC}n$T|-*(y;JPqk%Fifp&dB+eAMlLXh*# zciZ-l%nuJU{3Y1pV%H=A$kJM3`Qv!#do*WIke`418~*EbnIAlohvj*A!t^~vFUZ(_ z2=Rlp{1D>d37q#3+gN{r_=yUBPviN0v~}#iL%SzaKSX++d)Uwq5gwic zc#j~A|5JJX^pyH>+UQR{QV-!C9t3m`7eoFN+@CzKA8NO;!hes3UzF|#llx)m9!74yhZ|u1XSg4f=!b9*!=v8A)p7kZ+z)Q?L%4@Q z^6udPg8vNngQWWq?qPhbdpLBFe}?9 ze(*0J!aekDzK07`_-D93Je?onJ@i1l$74|a3GdHY_LC&}X|DZJMm}`DyGM>v`w96M zLz?NA0`sA($333et6$*#RMMZO@h?mI_sBNdzk~euQtP zAm6(_{64au-tQpayA%9Aa)|!#Am3B;e;+x@;CGPkY45*}9B24D$oEw8-$#CD^gGD+ z^z45^{!VhfPZ`F)gZzi2_;07mU!n6&ehd2V?T?3K-1}0On*IU(ked5Pz5kxrbdQi@ z^S3mohmH4e)$<;t-{+5F{ACUFVfKd$Z}-_l{Qk)Pmu0wz*&i Date: Sat, 20 Apr 2019 01:46:31 +0200 Subject: [PATCH 083/223] refine ResizeWorker.pptx --- ...he ResizeWorker.pptx => ResizeWorker.pptx} | Bin 38674 -> 45342 bytes 1 file changed, 0 insertions(+), 0 deletions(-) rename src/ImageSharp/Processing/Processors/Transforms/Resize/{The ResizeWorker.pptx => ResizeWorker.pptx} (59%) diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/The ResizeWorker.pptx b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.pptx similarity index 59% rename from src/ImageSharp/Processing/Processors/Transforms/Resize/The ResizeWorker.pptx rename to src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.pptx index 063f72de84022037dcd5d44331b3fce6b12efe3c..66087ce5423736e5bcd615fd68cc75f0024778f8 100644 GIT binary patch delta 14372 zcmZvD1yCN%((a2(2=4Cg?k>SCxLbhWE&&#Ie{l_v;10pvU4y#>g1g)0eE+TUpL6T( z*492f+uKvKwc9=Y>~vx!WX?7uu8KSqG$sfZ1P=m%$UyX;a)BlY5J>K04X!36P-E6Q zm)_Sy5LBZ-?Em|j{z5dAe%l!}K5kw8r>x*nT6CHUIwU)cJpaj6ou)%+w|8+0H>t$_ zHPKIQLh-TkD$Y;Q*U1s-y@hxx<2?@CAl)iW0?yAf0j^l7p7HKS5RbN$S$DkMr_sCe zz5bqeI;Lt2q=HIFGa!Q!HqzrnAk>=4bBJ)@KwDbgJUMGb#v;QkOn>Erg0?zYL^(_R z(fYX?K5@W;#Xx&4Ze8(w>{lpkH|xsIC2N?{!#gkd1VkA&xo4?5?Pu#pwjF2ga5q*> zw`9e+S(}%X&=G|;NexnC)4x3ly3=}<`N*JMcI=>$ z-^}8LP;$dd#^)oC)w^0V5qTeramqb1d?#&7rI~YsbPCeu(!AbMOVVz`V<2=%Oh{FW6FW6L z;)FstOqA#6f&_s^!1wq>z*=lRE9T%C&yt6gcdHYsq$q_FdmYQlapS`|(w8{_5`GXDHlaa6KjVv9nhj-v4q68L3DC12=7=V8da zB!Xn*%z}$dQDq+TeZt6Zuyffy3dv7vDX;H3l@z_HLgFM!*Pvi-BVzt!yMeMlwpzH2 zZYoO7D#Z0K;fki<+h`K(1C0^AZ=*z{;LiY(0TcE}adVoYFK}47-dE{&Ax|e)R71R{ zo^S(CnmMk!)0WMd0)VZH2SP#Gt;%uaCqaZRv7$ThkHQ>Tv8MW{B0dAoPeN3t7}{N&ywzVP%CZkNCYVvmH$> zES5{D?q-9u9mq-jM2cS9M_0V{?BsE|>*j%5j%Vv<`hI^Kv*E(7Q)puxGIbB}LR~@@ z*sz6jN2@?=Mc!D&ZXuEn{TFz@d@51Bg<&IiFdBTGRgYzazB6FKS*=1OK}S?{z?m2G zimHB~g-;ynfMN3%4UL4t^Z@m56?Ya8&?BqNK(f0&%D3cggB1Dw^PN_WWfgmkA5r)X@O7uG(Qm*H~P)-`oZX2bXCuG5!8Alnka zn4F*3FS=OzTU+|2h=%6s!h$_!-yu>bam8a_iZ6!Ysj-bWJ?SFG&MhO`82(|HQ|U@o zuXDA~&&e+*7F>;MqzG>Nicbe-tUMmtPd%_*|HZvh_)8~!%#OqD1Ww{{e!}*dEVJpz z=e9-x-6PeglgX0gi%9Ct5n%A9?|lID&DiYzx%i!E;X2b{6*UK39X;-umpn>&5ah1s zq^f)Ta=Egfluo(#IKAr=lDB7YSQR8>$%$nFynh_uvwQ!zmR;x`ZdY0>&*-jD3Ui zDSVZCn{q+gW+>xS9JF9#nJDAgFYo^z&^7S9gx($7+&W*!YrVg!^g4Z`i+VJjOb#i|=-3VmuL`*}Q)-f*)>W(Xv6NIi zQCJ&%@2WXb)+oz%vpT+1T+azqMGzUSsB%lSlHYvK$Xm2=OQotbPU%#hVB*vFa2wA^ zl#NebcgxC-*}hn;<+U?37y$q98Z#DH27lu^1l)ouue~xXd{?a0R}?wy9x_1t`|jkv z7Wb}yI;7WJ5?JV!XWM8=Y)H#g7F^Uj$W$cKmaN^%qK31}?uHGSTv7oIY)qY)SY8q% zY|YK+vRa)QTSH$*tRs=S$HDpVx~ONvJj&c>!qkgzPt;M_RtnDNqhSd=vZIH@t6y$- zTTYL-j?g;AqK(%f&g^vb#|6h=Bl!_(kQB{DyHx(F$oL~Q!{#AS*drnFMK(yTMM3|8 zx|a=yPbJt4J0_C$k@W{m#Uikt%Ip|}4_Y+cvCC~Ws=2EP*U{l5@zB3QVTxg@lygq` zQG%qxCAFm$;I6S?j+QmNzFOomXlMVvvbSwbM9}29wv}D!@WzsjG9tHb9Wko@u|{jO z0Hs_G8Ha-DnI&%p83I8w&=Ug!Wmbut8iNIzBL&!0 zRzp;2V@N)I;QL~iF@?{qs|rt^s}^}Y3OfDZkp?ezP?+6^<{`7{CcXa!ntV}$wY{la z&|qMvPaO&-?+<|qsrv{Ex&I?0NR1W#dnE2H#R#uz6})&XUjFAwEx+I5Qvt5u3TDyT z3m@w4t_h~5FaGWT)yw|TInB9HWtEp4#u{sWssy(ms}BwQNSGY{ZqG}6><U)K!G#yhKBd4pUQC$cI1kr<+tQ`7)Y4O$EId%|jn6M?0MuDUqb}yaj6*%ph)9 zynWfD4GR>nYI+_QMq64Iv<-o$h5X9WJ@$+=8X_A06h7|&UaWB2rFN?YiKUKjeaqa{hbuc96?ock7A4TPAmDLv+&_Z)3lTDLpeI&lj%_t z+_hH8YIA1us*>zv_xNO?v+609u%8kvb&!|$m?KdLC6NI7gtj8U1#Lyos+G?rhpNbBZ`Qd{Z&DIxJuwt1!FKW(ou7L_4za*gL&rkDyT%z9?-?H|Q@_ zuz8am)pW`bdaay?lc|^)U#^h|OQIp04cnNP_)?7493HO18X*Q<1N#}8@RO~bUwM3w zK8z~npKeS5W4YgU0|k3A*NE-sODxGW{pNzFE+DeYct#LrNkctz-B_ zx^W-?A~S$Q_}%frfBA^j(q5;4*rsi-1^B>B-;gr-&W68)xsfx2*x{H@Q}~pqQ0NSKPVewZTdW+H6!Jmy zZymf9I<&uuEaw#zJ?hejv-Q*yBz5X$?QH`<7VoWda}A|mgHNgL45KLLcCzh5qq(j{ z1kY!YIBxbS6;)K}kqibjtgg6l=$`w~MH3jLlkI)$`FifpZEc=<6S~y)k?W0Gt8VT~ z2VOD-4fMo-CLyQEDL|-JIaVq$b2;Av}fY6g+3}h;Y9R+B5s@C%Qbp~=W zt@A2U)`ciu7P`?C&sf!~vV)=@&j&tREa@yx=au=30#`$?RE}pJ2V1P4c|K^8A-#Np zw1@lvcRU71BGqN7{iJ=~weS=Gg%z&~{O$`$!%9;W?-5d#w-fq~{V~aG^+ct^n6p^X zxamObyqK)UuIvM;?Hb7W>GIc3i_!=^(cqE@m|eNn`C*MUd5;vpB_u^*PImH0MPc*SGBuxpMlG6Yp3{l-0Qc*A(Sr|RLaHRcs!AoGOY zZ8?&uRR$*Hw`}iC$wH4EfiaYifFEU=u57#E>`2D%BN~q5C&?-9UcX|U@61e5!GVxs zKWat^)ix>qK$+%)LTRT=HqXq`sRqQ02h_Z(C85TvJ?t1Q3AghK3G?BdBB7AUgQ>pf zVTy+Ef(RKTzKab(zNDD}V|>r-K+jH-R^Hurp3hv7cqqP+62?r%U;dQ+3jF13o(Csu z9zyu>yn!YyR_9h`mk8U-JYlC*bX_HTK10G*aRC(<_nw)n9}ZEv;ciNMQI`D0*huay z%@UAY-yf-?VL<3MFGlE|Z`+L~N1{}PRKeG+Q@@9w0!4<)%hfL`J1>GWfe+$zTH74U zx$QeeK~!q-zp@O>5Bl2IaM3%pzH)IG$9%AU-6(7FiNLV#9$><7I1g?)DChOxOjQ3f z%lxsaTj&qqTd(CBZ+Ic*ZQ#y%R;1+}Z@47p{Ri$qgYbuzSG?gIb9drk%i5ab3CF4T zPrgNEL)Lr?Tv1UZ_b)cI0e~_l^pHwKpud=r?NR)+#-V zMOWPR2;>6ZX%kKw{^&!=u<7i-tRomi(%kFn$^*_QaQge5ClmsTm`_x*gpZ8z% zS9Tlcm9Nr?geuo8`Mj{P-VSrERwm{owAh1(jX8pUQU^%F{_R46_*f4TM#kL~M*9>3 zTb7+w!BVYDhBHHS;=UQAotR8W5`Y_e!Y8W4NA@&&;Z;-7Y!LVbSF0vB?&%wh@z$M_DjKj13>*SVxqK$m= z^mXgcu-o=$3(e>q`gG5Rf_jD8)+!Gyt}3C3evqN_WBRSk%~L^Elth+manUWC6dF>6 z_xxvE(F9UaynKihUUWfDH1ZYd-AaKz>tTZ>kM?_qiVjQSv+IdE*W0s+^<(n#)8|3~ zzthh`$?+H5lXZZy{l1#oymv7!_B^-eHUqwO#}*WRwpQe))UKE1=jMKssXe_V0*apk zB(T2x!mVtM&&y$AHj%C*vS-@09X0_!e{J)|<6B8G*@TVY=ZA88p=>PyD$U>=T_ef82O%q1o1GC`+=IrtMa1F3{Xewb+ox<=(oS?UeiAO!~D{F0I~b zP+6#mWbjRKOW{bZ{<=?|fVBO=e9 zt&DA%ZPqv%UczK&=Y6B2K@lQbS)0uL82oo_c1zVp}| zC_hGT8HIq{vDQlAOr;gu9G2Nz1C4#Ew$}0ti{)-R7p`os10E|k9R=U$2iJ7wkzvve zLFtZ`Hhpt1eT#+Bw&O?XjJ8h#xLyKAzfTr*U()6esU7n0XnY+$otb=Dl47e{Xn393 zZDva5A<1~mfBoIG@Yee9+cOI9ug!My0s3$DbYvDF^y_Iun^VX^%Ke=Z34=SCa<6jB%0ElFV_i(61shxY-z`lk&&z6Q!`2*@rAkS;~Gq0}EoNxa=z8NH(FaJ(%pm$q_6HyXvoD$<)t`HqJ!{Z{>oa z=R^zu6ONAhuQ`i5=CYKaW|=AoC^YW-XrhBNSwa}Kdwe=6s6NV=c+Eia7z#$6EclHy z5m>6Ow865>e%G`s)`#|6T?U(~pT%8^I30BmWYMN=7F@ZdqW86pu3D>LS>l^ zPw`svlh_k9AOEh>3A6{O-f9fe+pD~&Y%Z?>Sj`pMNx4_@Dq$xRviypBs?(l4)=t8* zbx8VIH&uHFjAtZpp13Ej5zq4#u(DbYB8%w>iLBGd8Q^(!7Ot@wmQQ;C z#FotE7k^1z{bYjVOXpiUyP3)J!cTTqWAfKGrrYD#ij4t!Cj)HmXSNOGub*%)9^lu* z3R^0LM0iyXDQ_)o(S!2+`%?HL&BwSR6|)@89LHoYKOGuk(w=S6u;u9!cgKki)A%s> zKL)D8KegJG2OZ>T<`8NJt#vJ%d3jRqjI~eC>^YsZKt@o{We>SLRvDEN{(~N$S2)CAee0638qUT->mFn&Jrhf-hw2p{fqe1n|Waq_IFqa;JR93n&0B$tgE=g2Kdy)T{V3@JTvAYMU_UZUmP%Bs>tk_u&d=q@)mc~6xWH#}cmPn$?qlV#F$DI==ni`S8t9#&L( zfZNO(r&ebrHisEE&hGeEDKCBY+Q4s=Yih3zmiICzb4&EHukf=MizU=UY0i)Ve3Il_ zWP#CXdGTK((I-O4={K{UfU3wF>GBY5rt^94PBd$n&#nhoCZ$*T+Q02y1*=iE9&iHJ zSYWH5+;fWrF^*O5b90f*8~-$kMA-S=e_i*gR%aBW)7ZI55$a|^uIZMw zLSozr5Lb|mOqzxjvqyy{l;NCyPMAXBU)u7M&$*o%lGeGw2Z?3I(1}JjY-j!`;DuWAMp=I;DL^a%^3p*wk&rmGweR zpQE`D8kE#z9f{#7n7#RW;@AMh=Ni_eLQG z^drx@+tuKwDY@d)=4Kv=j(tt>-ErtCwifbZ_dmwA}1QpqJE# z+NF??DL#Y3qY)6Hv>TB$(B&zhV{S0Qughbz)P<83#lExj17Z3s=P+1$wun8Btc2St zm9)NFWJ)?ZW2Zpu(nYm{qHeD82;zLt$9EF1t@j8y16TL-sB;(TVZV*;swWC(lhN2Jxju*Cqj2%BZDDAmb4b{yS8W7Ld}7j}7rdx?B$HVO-)d^x1-PxP9iQsT zjz5Y9Osht+90tD?@l4EK?q&t|CW#B++izyN+p0 z@!Vo^*{tTJ4U53a;x@?QQ-Q^CS0Zr+9 z-ENx9)cwb{q8+_Kq?sS1rp?`*xbGi|pbx~Vi)WT-$D?%Ix$m=>U`5&&YoEqbr$Kx9rSG{KdTT+KLv-Tfc*u!KH>5b;K@wgB%bUdzi#V9QGjHjN+u;pWgatnfzCiRW-9OLQh&iSd6 z@E3{2t0=(_i{A}KTczeI4okDLKKjX{<0GUxCUxO$^KR2RuYM0^KPd*}7%eV6HZq51 zP15I*UPlwn!rL0#>ET827ml`bHX?VO|=wNo8jFo$AtQ>#r9US6@idk}#_ z_@2itrBHTi)S;cG0u%r^bJGUwan;P)Xj{zI+o-XW>L3!9O?vGS?A66B;SOyTm3^B} z8nZQoU9k4!{uCx*?6khxq9CJlRo8XCP-(GL&`}C^T`>q2G>TPhh*?9LZKy(ECGHJk z6xbOQVEOi&1E+3fVs^Nkmgi%<){o3)>kZUmva#p-mWaq{%MIWRhZoW%Rl`22OWQGchrDwsGl$ibb+i+MVFmn zRY7LQdL7kvV>sIK*6F|ax1i9?mtI1SF{x;OCDLe$9Nw7dL^3t@{DkPlV41HnV?y!@ zm4R`l6jYXaU4YDHG^cRH1Tf-tdxSCfk-UPZK zEdyREPdyx?hj30wGKEB~T)3Vg?&P?`6pK}Q)Y`i!@tE~E+jO_Mk0uY*QG493mEiB0&x zw-0Tv`>fXWQ-N+`L4Se;d6fdv=mVrcriTFb!AwCj48=*AUJy(|LL_Hm0u*OxMm}pE zt_~5+1fZ%=g9#^D0IfKVB@&7rn-|Q2CTHwnhETsWLF}FJ{f}$31SY~5YHBEHj4w#} zpbvG+JbYLl8Uz1+8s9J4xuX8ijWxg?erBda+FIfyiJVuA+Z1726}|gdn*tmk?}V?{ z)3bo3#H-6+#ltDe1AS>_MB$}InU|jhd2LEk4#ZHHC-sN(+n8p3>Kzutpfuv+bwt{I z*_x*3`L1z|Ss{*wT&IF5`xSK`VNnY6`U`E7<7Zk0I$>$(*rB73mzq1E-b|f+ur|;V z+ed{uS;n5Nudf-v^o4^|P8HS{#=M*s;x8Q~rotG-o~SQ7MCt%L)i1Ez8T=5H;!LR8 z84$Tu6&1R#b+!(3JTi%5oMOckZN=I;?W8LGY3!gdF#en4I8B|cfPSlzQ%jR=lPk34 zN%89~1?oCvUF;Iij?0I>6xt=YT!Vd~bDcHY?xKx(WYjpaAm%zng70Ea86G{*0%gy~ z)HUn%pM{MF(C&|Aar+_9YszZ9A#5EBFo6LZEJzt!A2(Y;+vap+OcNdKT&18mE>>5g zX;iauHraSnLCLJI2z3}Iv;36x!L<1(csrYj4CI;TY-ZYDYX%M_;m5J8dk1^c1{zjO zTfCeXe!SJ5&cAXSM#Tj!dK!J8VX@zt8&hXyY8z#^-kJ0$m^iS$8R&RBSMDqhRR)fP z!{ipdJ@UIBl);Eubsc+QoGEN3ywZ#VXdr!7b5@RUM?ay&a)o5-q@}4# zgEFqWY56YC$cOL)38@T2^~a~iygO?2^J*ek$tvp(kFPET__!}EVKHti2S9}PPQy-W zL&YO{!Z${V;d%C?@+v&J5g3;-D@wl9a2ePF6&dI@VjFSmr0`GA?0+U;LBHXhq>(i1 z_X4pjr4c3;BwQE{WJU2)GhC;IdNe-tuUzjAkmY1$nr9l49@y9?4n$Ka5l|NIgv%3< zJL2P!^C!ki=M6l@lW8f%bOW`+0vk(7KYcJG(^5ahQ4W`K`SJ+Wwin$H@#s7Xc(o6H zKW$7>G8x_7|K^$Hf5UY)%u7ZY(JhFh@N|dG2`PwSZf7oLs$R+_L|d+9UIBdvYMk*5 zs@9yUo`@8VK4hGvx$2)kpPk)$zzSFkenNpdp72@yuxPNqkXzyPTNuzQw9$PkucE`R z_*F{{>BC{vh$kN=YN@vyjT92qEj4&7MLz@`){L(l#wO9-d06J#u=NLfet|KFEy6J& z^yF6-=|q;;zyKivbFuO260e{+#OSoJz_R5=l5azFA@{A0(T499Lca+iLZ3K-;jdoU z5HtvuG3$K=D^E%HiYGv5*;3}|_-4sckFkh=&u3ZPsB*(y5!|wH(uHAN+KE6e;QOF!j-}kT?J#f14U) zdvh(pBQ7aT8xozc`3D!#U#?o5J3$sx&1@W88y8TdHtw%Hp9Mt2Pw%;am{)(Z7*tSV zM9kgnmg4DTi+>Hw%`{bV0?8;PFXSznatoUn^d9(yC3iGlNT`f z%i$pf?2FOBbeiwvj|EDB^L+9cDUJMGOLq*`1?^BY{3(*cxW$4!*I{xl8i=LH5~8Ui z*$IXF=T~w)5P-ct#}a%Z(r~q-DvD=_W*#CfM>qIA-}2)G7IlAu5n?Z+s{keXFMLt) zvx_~)S)>IpyzII*Xf0{FB2Q7NLiY7uNqzn`p=(&&p%rsi7!y=OS1D`VxV?&YLoQ{c zxy1dvnx9S$gpOZ+hnf6dXk#!LfX6|XM6iOb?BZiZKmpKtm7JK^+_x&-=)wY2eQ)o$ zL}gjKe|Vq^=5u!F zn_le=l@C0&3{?p_Ku6JMip*v%eiLh?I6H`AEKrl*y>6gPvgn}CnJL= z`1(_0K=PjM=i?K_pcMlCj|lM(QdI5x@Bb$(&Z47;0KyxT&>ct!qYJPNYKR2ckIK{aB^#%4HXViEHPFC^Zi7} zi!ZUd6$=Xtbv^Xi{hox5O$=YTvO7B~4-j1-rtMy4R|_hvy;u4~_^+Qt{xzvafRq}O z#(Q6tvRuav#{vQTo|fxn#f07b*6jhmzg~Fa8p*g81K)}&c?4yCVojg`j_COGRxi79- zcdAHE98^6R+O9I;zL*mFxd$m@erVU5ai4v_0cKd>Z_%iA2zYxC{{mnHJZuOKaE%cz zm=~`NtP_J;XF{Z=jxeZTw}A@+ZQ$0mIbweV+*5f}mR5#^`bwbLVkRl0u|9hzdTXWl zC|u0PY@>Y4G@$=;!}>+dzJkC#&!P<^rPQ=xq)^s@msTNQN0bXLmdn9e1PQu zA9Q1Fr97tgeDj6VY*Hk@ac$Qck35wnHcfoV8l|9Rq8Y&)+BE|`6yWXSR0*1#&us76 zsoC+r>Q#s@)Beat|C0ErFTvI*`4U&SH^=C@brHWAslt?V7ZDhBcpH_vK2RJu96Z4TfA(d`-g zS94?4`W*(eeo&lps|w{_cyy05+p?**z8$s13&`EKR4A~RO!1Jx?Tv>wi%X6mCEi?) zDXlGO4(4!$6$7iwkdL$`1XTg0S&>)WPa>XJ25s#`+aWvJy8-rli-$iWyZX8$J$s8=^H434YHb~&-PE^5(oO1rv z*^a1T#?p{kO*fn0<^_kJogJpR9&GNDEvIo#Uh)3|4E*{Vda{*Ff=Zho8|ILjMLr zHw4OGa!3&0KeAd{+Wbh+V}bJfus=9F?sg7oL{=5Ofd)ewU!0A0>HC2Km3!5Dg4K7m z8W=C-{;D{BhaO+=nSmpTwmD|XdLCi7*1kaAQM5pVfZamRYfQgcqk8PrCpI6dj*HWp zpKC|h+^MK~zbc~WM+UUu;Tc7{sN1xF+l)C3{R_)5H{9uh*elV{E!AqdutaIltJ{tx zZc7^BM+GUC2t9y1_3)b)GjfgyiSkFit>oKF#n&)Znv>@5iZDZ|J@_?^lImoMdd}sQ z?7SO86c6(`nNMG=@co-e&SQ?y+SuV29Rfn-7y{wJ31L_l2QIgYvTfrqNyf z$<3C=0yhBgogJwBKo9XgGt?)s9B*V?@MSOlFE$ID#%DTYn zC}zR8QT#&0Ooz@}a1~ga4iR77>mAt6_wW_|dJ+TZNwpL1w8bjWXdEwnJ-PgmDri4B z)&|iH2bw!E>-N@cEtM)Z?4@jE6E_y8c2oJ;NDMLHvQhDcrKbv+L0l^H^Py`ec^_dmd$Q4oS~2qH3{mpu4>%}VTta68LbJtNTM@)(9*3fF(-cOgpK+tESK5C( z%5Em_5|lT6mCV6gaiP13ns$7^H~hX$0g#3a5sKl%QI7YO`mpfE1IHVP>`LZQQZuE4 zG?w7W+U-c4a@O`&wyONV()v+-Aj)W)@iihK0;xzcfHb1shg~W-R{N6RWiVJo=oHI$ zHtF5?^I2qu(@uK%a)OR5aZSTa_& z*V^DSY=S>bEFTRB**C<=2=@)Ns{lNnk=#`7HFZY0*BN?>?F8acgC`{#xEP%#O^g7M zezc@ZL0i+xHNH2{0prQ2uJ#Di8|XcfW}FEMm{f2&ugQ=l()dMlP9>)U^JS@54i z@$2LOFqsZ)vOGuLUUz2u0)HQE{?p|I_RB4<1hPtgg%=T1UwFhp2_Xq??}?->L%V2v z#;ST%XzVk(MW20lXL(;!2Zz*b;-GP$PtGgyaP8Wcf4qSLn-AUHftrpg8=+Oy)wRPE z1_z`>7_Z1Nyg)iWQ%rQHDafy>1MRvS8ja^fr|FD)kK8aCdsIPYxHAqnqPdD$kndWwgeA%kA2^TMphk zese6(591H-9tg`IwYy|pIdUEAqXQ+a*-6YcfRdnSDR`R0-RHZN>Chw27-aw(RG@kZ5y|0YZjBR51tL%a)Jm(SQU!;+W z;F?GY7slQ|zd~Ac@2}(nm)Ve<{uU<;B8I?2*McxzagP~}B6oc(&vSb^QCaNHxK7xr zn2qB`Vm~)$pkweY`!woDgBJ=Fy>tqZbwZIntpxb=0P5Vo-VaIuH=jE0f?dt$>kxx7$ibblAxv1YUc-m3j{+Ch?R zaM2uEM+^XaJ_b_0Rc|0flrE8LmiKTGfB{^%0zEF{E%nrU?bLb$iG6)6c*P{~2mTtyy@4R9-$2`k zdvWKM-5JOwcEyWUPTgMiPur;z$qfx`S=bcqZYG|})L3DC>J{_}^}4>^0=Il@K1LyZ zCWopqd^D5`N~LQP^O3Ywzfnf&`u(RN5MXWwGQ!_eCVFsgARdsY=+MK86nX}}CCOiv z`LJ#F8MEGcnVFEf+dU3lo&A?ACLHbaV>3xLx%=k)VwI7IyO3Uz)Wq$ksvh{#k7W-j zx?V1m@sH8bU6JJkD|~^Z{l_2A@IyFPgJJ3NxA@4pISB#Xl0SS10_F4J|6n5q2px5) z*qiy1K??a|)3E@^=K8zSq_KmlWc6Ei?VUyI_T0KXnaQn?3RVh&yX2^X_P=`TGFu17 zy+mtE%Bte`II~KEA3Qj==O1Uw&KYKA5D`@m#dAgH#Mu$%gLHeSSp}z&X)cWW(idT# z^8MU#)rhZ(84UEa7>ar?nv|p#Ug(+jbh|gyE1l$xhj}9N)x~$Z7ig zhz1YYawDMSrA6+%dhq71cUowq;u@#_s{KJfax47EfRI$Vopw3xy7+ZDHkp0}?Jv4S zW|Jeg;et9Y0cb%bbiz10p~~s$qgLuKj7O6CEkR}Hi^{uiv-|~Em09tw2?}Z#<_a`j z>4G^T1gU#aWBZ-|N@T$%!D|Rx5dy9B?+g4t*KLK7Kmb2J%iFK8qORfmC4xUOTUOlR zh8-t$H1T{N)*+TyOI@&d5j@nX3kLP1YuawcV*YnrMY&B;os!w?bL4f z9d`B!A9$neQ28|pHjDP7J9LjH_zxbI@Cvw0Q@tuR$RQ8A_vxMHS2*J{OW}1EnVLd0 zs9Bd4CAqrHZbmXR$1en^x@nNRSiLF7uzMnYVE|rx51?;{X|^S!4i9t%0)2jq{#C*S zT*e$!RA~m7Yu~stCuwy5XnYT_O^5%8ff-UH{!R37zWWR52$T{0AJ#hf0?0&TqKiWu z;s;jpyB-O;KfLt8>OH~5N@X#KN2_pyMf1?gn#7a^6?K7YtIV24qi^Nitr1*W(Jmm8 zO=7^uzcn$gkKXG1ZNtyvGe7ydRui)mcso6eA`m!2G`vw6HE48+cy3uSjG`7V`&P6b zm06If!$K&E64bEi4fgUih8P)dCOE2MD^QSY;=*oUlm%U*U@ zFANsxf?}%Lw#IL_|0xiG;rZP9*}P67gc<@cP!3|n3_VkPM)`54vKZC^tNlDwsV}}A zJY&Q8AMIK3uTd)tmK6IWs3N|vR)K!mlJ?-y zS2v_F@u7m0OrY94!_cTd-&CAnD7v!iJML;_iZcPPc(Y@P;_x&; zG#;s`?{kSYC5F2`g&x08$Y&SqH%BlmmL6rO`;)F_g6 zBb$ttyTZ48lMsm5W&ZLVQhP`au>)HK^*OceB+KC zV9dzE6%bpt_j%3yDe_(l_4Wn@Qjv#%WCH#Filabaz91eY2jg6WdzAz>Z=5vz?5 z!5=xfo&%LqloMBkBA25gd}$T2im+tO1F2Y4N33*PYX#BCnT%K zKXCcWziuN0=O!UTa(n*Mv}ySPy#C&$kk>yVb21mCq&L_inTFus6dMSH^IwmiKLFh2 zhzIUUriWC@03Rn)5dJ$#LxDhO|D}tV1!hPQg6z%)I|O5affQ8wf5#0N5D4|ZbaQe* zAae&Z6=w%W7ZwvoM{qzarqn;a;(Y>8_a18g+w^PRf2f-|I9vQ{je?3i4D7#*(!K8v M?{9MYkAF%32jo|;O;WGhM>VExE&;r;1(R_ z=j7Hs-*;~PJ+-T!Uj5ees@Yw=)~wFj03}p`NL3Y(QHTIE00RI3TAS)z8X%k)M}^4@-!PdaEQcom7}{nM%rG3=+NgRX;wi+fj

`8nSpyB78b)F_ZaiyHo-bs;S_d(IxO`GJ)>$tITagFS@9 zMCl_#U8i`JPtTld}G@lV?456j2F~$ z0_6jRwPBc#_Y_Om!W-yriH;A8K(V5`sC$`~Yup)S@xwdDvtUbh^HqcetEC%7zpIX9 zQ8@vZLCU96HOZn30xn*KL03J=BHe~))#Al(YFSzBo@abDVm34*jiqXWg5eCMOc3slux6?*uV?Ukqy#x_HXok%`QcerzK#@+4m`c55;3Z zAc@^QfnaZvXW)Uo`GL3q?@o~LH%Fi^cS)$&V&v@8l^pp}TgkY&3t#)D0|J;@_PV1}bycUE-`qOW@2)P^5I4C3x=HXaa@ytj=TOf& z>!odLK?92@-?XaL?@ER(bzb%#5crr5vM&K)GEEuBOQ#--b?|J+ri}_IAgp3YdtX`FKy}O;4QI|S zl(ZmWzo^tUnJ^}ixI2rXsr;tu#@LB;SgLY#IY~6)Js9$2*n1Y7=`AUK?<{e`pgSDTygT``UO8u7)H6!ofk>ir*u}k zES-KHoF_|PerGvv*pbnxa?3Yp_2HGe9|O)>$Om;v{S7?XCmZy&Uws|UD?8vrwTS9Z zzP;%P^zPx2JxHpunln7q_Hg^F(f;|2#XxNR(6a^?ex3KX&)KvOBA_JuROyEUas@wh z+{)DrM@5VH?ziNJzPI(agR)hax+`i9*jsrG#QL|}1oXK5!^pdR8CHe#vR@%tq7MmT z)X3W;jzGT7wioNK4CfIk@y9e+u9cRXhKC&x_MbbZ0nF? z4>d)(+C0cDGHc%^EYIqs{z^~y0DMR3vzYnt+hij`Y2yT$k!pv4DgJ8!tama-8e~mC zHiz0rRM^{l=M+AG5@s4Q!**oo&|968a-G%Epvr-dcv1gSL>U zO-sWt{aJ>GbhWV-)l?nQ{K|AZfymHdw3|$a?FRq~fq% ziR?(2)uh~)(^B0<>Amv4?e)S2#ohV!%b}_Ki-Tfc%{yoowDiES>m_&`|u{jcQeL4Dc(0K~;qu6z&(j-#JjTxih0MXcG^25SJ0%@>XZs zM^NikMCn^Pa?vhaKU^nnSnoRp8z743gJ?GQTq_pRB!x}7@wX&j{Oq`9XY z0r1XQ{4q4~w)o}_BEwq_anl`rP>;(t(A?CK+;V*NrFD!qyPoer5;3uO!xg?;_JOIw zJ^+XJl7gF(kq9$@$hmSl1W-ani)2Or;UX)0q9La~Qh*+-x z2ezrf4JSTL4}XmZ1tJN+2 z?k+eiaDGdV;o(1J;^e@4FD}y?*!yyO;0eo!rn%w>wQSlU#>iS{HGEA=#L*!|G+jlV zpx2!u3D;!CFl?*ZR}c_A2v7X}x_|*Uk)HuG%{U04y+>zI4SN6!ArU?8e@77(;Xi`T zpaP9c)DK1}_xFhA*nHaU$-lB#6A#Ev+nZqsM;LXFh%1gNMS)G zM;XIqOi3r10x^AZ75Ni=`K{aq`hX*o#gBFFXI&w_O z(rw>RB|fGVjY-JVn_&*Re!^M_S6KMr4}WR9-Qi_Ex5;~Z)b7Bjl_EWkGW~fha9gw2 zqMLnt)ivK8IU`#o_gz@jm~a)l6}-e}#xvnk)3NL-SEvPn1f%66f>ORW10ACQz#`=D z`~d)*oLnI;b~fe~F8|YUz4WxV+t*!jSbF`e)5v%&aN=J23_<^$92Vb?iFM`b#rKt@vS|T`b2wpZSGxhus|BmTUep*luOt z-8By~?2br*YU_wV%FbZl25>Py5mp~e*=8lXC1opCow=Xh??E3n+bn03UB<1o4_=6* zy=bdSQ!2J^9MQJ9Eps>>I%l`-kQZ_(A1`*v^yka38e$3kq@r!3riA8qk9Y4~3$7C` zgGXr^y4}+VLZ-*wt-g#ZqLh74#ig5nHjA}c@0fNDU95N^#I|#>$Vcs(wWQ^C{rZ*F zsLw$w!sXh~r*HY{{f6eRp=Jv%Nv=^h?WY}e+Hk(3U31gVPmkxqsNq98*$bHaq*fZ?h^|P_JnR;8ZGtj2cxw#l-m z2A>uEp-Kc4HT=0Wob2Kz;u3fos051U(J}<4U!m~D@Nuq8Ar-{?$F!m;ub!35py_v> zPtr^|?J~WsVgMc`hZ*}lZ>%H}Do%iU2okFt28a;@!x)1x#`H0(XzIr7-#*#Z;-s7v ze&KJ`&NsIBoaF_WGu4?>Je&aINE(~44<*?T%JV0yU}O4KF=>CvRIQz5|5>sAq{YSE z+oob)Mi$@dtz%@1`<#UX>hLa)5vj-gug^ti=2XdxZHmJ%xpWJ(jrt?qqO*4B$X~_> z=b@o(F37sSWYm)PY9~8uZ%xp=b^`jozA&%E>Z)j;b>g0zdF`e7wqcc5d?D9)+prVr zp?Z{RIctFW`)+a9k4qcbVI|4LK+wHFv#~o>s_*$TTh?(&}wWS3?X2%w29hh zm!--oN-AlK-&3{!+Fa<(AZcxrXzE|!R6a6ow7kpW36fpynEWlX`5Q+}cnn`=BY@^i zSv&-k-b(=P_M`C(K&E2A#O_5FxGbK6Zn2-rg=3`n42CH_rT1s{TNUbON?Lt2q+P7p zn5pHXPGuM_l}A8;im8f{hG#|`@Ffm42P2+zI2D!FueAP#H{Q=qmp85OWt5lcX-eDV zPWG(aYjM!MTV6Yhme#73S(JHD%c1PfcJ7|e2kr;%7O%GtVq>E|+^6^E#7pKspy={i zV~mQDACZ_Na>1CW7~$09ghU#N3H%#jQd&&>MD!I|A?3GDJ_UQPT9aC{n?GbUZ-_^b z_r)vTsgjM=VN77iY#&R~hWp90aAnf^vTT^rp%mWYKvX1_>5;pL(PDj8@AZ542uDMs zuTAaKy#xL`1|ps#3 zix((Cm>9?j02xDI8l`*=!4_d;J{~?|iIthcLvYQ(1~>w5txHvIIVWF1K^k@?BnzhY zL;#~Ch7&~@29v(pxheIVhoFrwwVy7Vtk~ANwcK;;EI?}9PtlMDo~Wj$`r-2(DP^lY zGh?FZKYoYL&vj&+>H)|3^cd&U80tsc6!i&DDyD6mJNMgbs#rG@b$!C*2j3W*moNi_ zmZoH%=G#RvxtmxSf`(I}Pp`!#aYPq3wy=#>q!gq_;=?Zr12e4%QBf0JbAB8keC;^v zUX=NY(aRQs?NdbXc8YkTZQHuOOo=FxowR2J`Mj%V`f3K|XB7(yTA87GvIxYbA_%Td zZi1(9&Nrp*>rwEdrA(}}<*Ys8zRvc7IR;L9ap^3<#}k*6(_gZoA8f|0WUr`6?~c}> zrTCYfv%h!JJa%g!xWvct5(Lticp*0drr$>Q-z>&)58~nzyb-l348re4J<3(4kyKm` z6f{*-4-BQ_&?sBWJy;0P@erC5JIMWYBGeE_Jzl4ATkd(fA;gPL)v;R44*7yh?Kl@Z zt#c{pd-=fp{%xEFwD?Rv+u$HKXXiRBk6X4v>VAlxHV!Wkp9UWbtNS_969QDcLQ;BK zzaB(2O>>XrS>u?$Lr0l^KKaw}to`>9qR8{_;peS;{Z}P$QL?R@F?7mt{@1hP?&RSg zHx69}VwQpDrpr`ob|3zFE7$oOu@(>az_-yuTVg}xm6OQqKy^s7Eo{=3n)X7o$k*)? zMX?gBn1WNAJy*CcmCqWl7!kFzVmmjNh==Omq6Vb9znG;f+M=PWR6Rd+kx7HaohgO@ zlyD}dVYasJbu5NxRWh_GTsgb$d74)2*JsBj#&)~oEe^Y1?~N$Ck>Xyuyb1dFiLXM0 z%zMN>x1NI5>~PAfZ)E0&plcl2({n>tdean#^M}9xV8ixbFu|&ep1|ZC zabX-UHemPva64u-Ezc7U5CD|JgwZLX>#=zpcmrKVAC4x@_xfb;kYbv@t@LdXKsd{9 z0Rz$`JC=gVti$QG`nTeclzrsm7j_hmvW4=*mweDb#OT z^&(egPXGFma4&J4;M46%;l;>?;&yV%eKO;3WPS`EQf4f2lxA?!3ln_S&{{f^t+wak zY?NSXHJ`Og^iz|uUQ*CC!k#S)VJRPcmsFkPKTj)@HT4B|lOs)^k9x|by<6)GF;7R9q=tZzONrlj{)jSE6y}Oc4q9P%@CMa(z=kwC2afhWp zX``XQ{zAkarT)2=k-!U*k$roZ0b*sabqa~gHB_Osa03|i4#=|+)}LZ0zWs4? z#9ZWo2&=t&`giUH-(fe^z|^myIl~2jGhDcO;4^Hf2XBwB83KssL;!p3(AiGyNCdDY z6@vh(T@b*O&M%AXqk3p>7x$M+=uGzI8Um2y<&}E>P=NqC5x@<8Y5CRb>J!G@@kNv4 zoV-Xjx}ZpK4sIC_0=PPEL;zI^cOT!C-9Ya=^`LW%50VwyH@vtXsRKK{MuN-n)MSWY z!Kn;TKpXoc?m>FsQGbE}bPwN$UOh71qg!*H2%yrpniv5LhLIxx!yN=r0G}EbTeC(0 z5&l+zx(J|L?5OJ@#MU-+?p_B0_#uFEdJXO47^4k^ne6(WbqN_FEg4iIHF4^lMZ8BJtIutzpNJv*d?ZrSqJgPJJcAiUVEp|+!9W%HCiPuJ90D-d zd}vsnBRO%HeLs#N>?Fl7<@M9Ozc5x{pY6w09#@uZOtj`wvDiIhR;=z+znbH9%C_&E z*ZLB+O@8vO09L%vfpu{L{pGhEGpq;E;@KQ1^3PKi4U}~`=BHj9dvm1z=#*@QwUfNE z5tlaFlN&l{R4ai{G0rXLiHmaTdBN;^)MVZT!BoSZ!wkZ({_g{qOCLwV+>&ec`21LTm4nfK zeh$@8a$MrT>cE|c;<*|fpk%Z?caFsFasOo^-oBz4QLFj$Y2IhS>)-hn%=V2`-gVz( zMLT7uGukG}+ylZF+d``y53J@dkyJWGY~bzQkvqIh1+v$iC&FH;ps_QAUNXhz;>NsjL4@tL=Xc zeW&@HwQ5JH!MZ+ZNA)mTw>O6{_qSNS7NIj5$(A@lAqRtV(iuPMQcQ`n`E(dWE;&8R z3tSjiT@a;URO0tN)7_52NlDhl?#f$}U1oTqw1WK2o=6dD^P9|XRWbb8fF9}DA&DbI zACd3*%)RhJ;CJhk&d$QnN1^Jgu1QLJ5@(W-f?$h;bVs&Uys@p5J3;sewEF_`Cqj*9 zuAqtcML+hO zrhts;$o2Fj)p!qjeMp|Z}8gq4h zLv#IN{pYFVh21&8o&c<#Wllhcem7Blcutj@oL{olsZ?=v0!ld=Y1=P0|HPUilwKFP zE`c_`a$+txo+6QvI`L-uZD!6E5o?J=UZEP^Bz=^zt&e+-g8!$6FPWimYigYH*+?Y2 zbSCQr9JgE9bNh?)WrG=#x`Rk$mb!#ua7~bD6&Z&MBZIdK@~r*HL{jEtptX)eKrUBb zrZi(mDaVC^y@6nn%3?p8rSmqG6-!S0+I=VjbPydFV{OzFE@aAY8CG^d9Wdpht$6wI z{M-A}dCP7VUO{YD zZ5PHQ^83||&wav(w+S+)yiCh}Mp~0&sMYxCaB%}I`LTqMP(g=s3PDYa#$r{dYkbUj zr#eAiy*!zf$(wB235Anu!fY??z&J9H1SWZCeRM4u$Foy{SuWQaJ7}#se^)ZTRF`QL z*cE5)B~(8+ref{Dki#?5n79O9MqCiHVN8h7e%sQX1zENjC*H-(Y5^-1&Cz)8eC~LC zx9UYK-8G;Se5I-RJaahQKfH9T<7rj+79TGy_a))ohz+^G5_29oUX4#=zYV{0HF3x? z^1HFv#A`+GmeV@cVCYq7^j!G3(tX)amqwYpwy3M0A7%m3N%_DPElxkYvtO>}u1QaX z26tL`I3!i1X_bIP2SQ&^o#;{8cdE1u{&{h#OlAqhSHi9wa~9<9$~eDXd$ZdYU{_)i z@M(q{MrXzZz5s#87R`fhm;EZn`%1SWaWIN9&T?{Xod&!_>&ey(FY4V7YIzXn3&?9J1-OfpnfdL{HIZ)!{m!N$^U)l^Pgz|@PGvrf+_xQ zP3J#~L3~(mAv4UUhzztx3PZ4wKBl%jpzY_6NC(@k!lU>nqs0Oq{r&H@P6m@ee+JVm z#Dndy<2_FQZ1A{U51%2^mNi@;Ib|MVCN8qdb3b z=@FX*{vc~91O$lx!3kAVn13lNh)DX+aPV-S`hGIzM^TQov}OP+)82Pe8sV zf6P6PiXhWJNLk4P3OD$D02!Pd+LVE&d=|6%Q!g5@u5M4wPf{Xkmo?^=K)! z{)0xgs4($r2&l>i=3LE8{!g9<03`qW)BWiSoM7;3DbPp&?5dic;-9elxF_)cr3(*& zy{ZubX@tPsYZ%%79Qcpn8UA<<{aeTJ5dh2`%~U@)I=Mj196wmV)C!5D|2eZBwzw{ Date: Sat, 20 Apr 2019 02:15:38 +0200 Subject: [PATCH 084/223] xmldoc for ResizeWorker --- src/ImageSharp/Configuration.cs | 3 +-- .../Processors/Transforms/Resize/ResizeWorker.cs | 15 +++++++-------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index 7cb014563d..4e8284c2cd 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -10,7 +10,6 @@ using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors.Transforms; using SixLabors.Memory; namespace SixLabors.ImageSharp @@ -108,7 +107,7 @@ namespace SixLabors.ImageSharp /// The default value is 1MB. /// /// - /// Currently only used by . + /// Currently only used by Resize. /// internal int WorkingBufferSizeHintInBytes { get; set; } = 1 * 1024 * 1024; diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs index 7fe2638802..00a8cfbf3d 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs @@ -14,6 +14,13 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { + ///

+ /// Implements the resize algorithm using a sliding window of size + /// maximized by . + /// The height of the window is a multiple of the vertical kernel's maximum diameter. + /// When sliding the window, the contents of the bottom window band are copied to the new top band. + /// For more details, and visual explanation, see "ResizeWorker.pptx". + /// internal class ResizeWorker : IDisposable where TPixel : struct, IPixel { @@ -127,8 +134,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms for (int x = 0; x < this.destWidth; x++) { - // Span firstPassColumn = this.GetColumnSpan(x).Slice(top); - // ref Vector4 firstPassColumnBase = ref this.GetColumnSpan(x)[top]; ref Vector4 firstPassColumnBase = ref Unsafe.Add(ref fpBase, x * this.workerHeight); // Destination color components @@ -141,12 +146,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private Span GetColumnSpan(int x) - { - return this.transposedFirstPassBuffer.GetRowSpan(x); - } - private void Slide() { int minY = this.currentWindow.Max - this.windowBandHeight; From 7aae02deada1d33ffc9bd2730dcb8fb496d48b5d Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 20 Apr 2019 02:48:20 +0200 Subject: [PATCH 085/223] update ResizeWorker.pptx [skip CI] --- .../Transforms/Resize/ResizeWorker.pptx | Bin 45342 -> 45638 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.pptx b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.pptx index 66087ce5423736e5bcd615fd68cc75f0024778f8..248959170b01291490b7ab8c89dc646d3c2cb936 100644 GIT binary patch delta 5686 zcmZ9QWl&t*mWG?gCBZv5!L{)~;|>7=!7UJ68wgI2(AbB2W5J zlgn^trsmH1v!4CFYwdmNRPD2>R`w>ae;ZiMkBLL#CLNp(1OQy0!NoDDVNQ!YBmpNZ zN7VFdYIcJ#sgSaY5rGJ$l6w4g<{~+dpM!!VB1bD;KN%U-RS!_NahCS3@BIYFg)f#Y ziFuwK*Q5UJD=ETF zdpGUCs~AG*k_g%3VMEWkg9&{~YZvmwUd-ABNtJxQ@gy`6TNFDL!~1?s!n1OHC66!W z$?}n6LgB%^aX?cRYaet*E?1D-p5zkuKuiJ;&ClPL$0}#;FMmr$!ou2vSFa{#c2|bu z;#2n2=o}$8_euGz*u>(z8030#4fa``wyG;HAy87ws4H#pM8skVF;kTA9Q5#uts*|u zOrzohiP`U&emQ>aJj{c+TrIO2Wx~eJOLQj&dC&vhKG?>-Z*uy2`*!Z_F|%W_NJOfn`PUoQ_b6!2_Irw9WwPRM%US zpB^1$AF1^5>(@D@^#MhwQW|7&ahIPXON}H`(thFfJA!c;pxnbG4>t)bRw@%$M@ri^ zp6g&)(ZIND@wSP3PWM!==hm2~Ipd7j3Nu#`M@w=Sl&vW!eU$CHiO z`f!l^OSn{Zk|P+dB9HZjbh*6HjR7XVnb002k<0D!Zz zJGYyIou!rAzXLzlM@NUB#!F61gXHbplU^^~B@cyZqdh>G8$96ZCA1-@sQ0c+A|RP4 zc6m{?7r2+NZhbx_{^<$k9I15}N$B~wgL@&sGJd}XqdB;vts0No=R<;KTs`^d*ETeg zCSd(LmLhk~e78G$BtJCjOX?OFIff1V`EE106(n14FLK&*Xt^Vm(7xD2ZT+-#*PXxE zLLmCCIVC4RYsnXW7!ztE2V&(LwYSa!rL}J-54>IEcvBSj7Tg674)pL#O|rAq2D~+6 zxyLUIRs=P7zS()u#GX1$eH+4J z{_A!PBRSsD^AK?Q5Q{vComh2X6_vQ4RHpq!O;zbmv06sGd^ErDaM(IBP`yhVDF!n~ zIB`mH9r$a%_;zV|`7T|P!*5sv$0pojB;nU`ZW7;7sWP?I`IUOc;Fe3W5dXtGQy+mh zG6+Q>~;L!f6K@C?OcCyeh7>=V(Hb(OuMq)F&)Gxmb^y`#j>f4Kt~Pp!da`~ zEb`)942OjUfwSBC`R7j}g$2-2lt8e37;xa$Eg>wZ@29ap&J1BQ)LxMPb4z|CSKX#% zQp|wVX6q;S6xH+J4V|a+F;)hMCVL9J!neipH4YgL&=$ucjlioI+D;~n_1iG!%q z_0R(7ln{cDtEzch20@4iykdTH;GyfI#}Q{;zA5<5QChf_$#b-Tvxbc?x|2xDA|a+t zCX-;d^mn&N``ZSe`LgA&wJo(e>zr1+Sp}UP=}`wgNEK!TPtRGRMiqzyWfrS1 zmY(r$szPb*N>B_t7=qKsknWJ=rlQ$6nkQ&HXE?}Ga%O+V;`=P#&uXu;E8!|kv3xl| zmshx1laM_7a$IXNigj>I-+7}{Jx$Z^6pJceh-|y^7NuXWZk5?(!<{-pu+qvG# zFTD9yEQHeHdiQ*ksPZ2|u(c2c*DzLu@JbSaD{fVvd&-9}s0rH;$OfFOwq5~!CDxAF z{myib_|DqTDeyZHQ_FfCvK3Y0upE4iH?oMjDPcKjRd0tX0NYpJQcE*E(cWJv;3`RB zhm1mi8R9XH^Z;6XQr(GwXHH4oAHlNg0Wrwfn3h*UGr{y#W#=<4msKdCH0%^a6~fGH zn$ZK{THrjdd|Cal-dFAfa1nmrhg)_ssPBDgJQoa&}`3_bQTS8E$ zCE~ab&fN_a10&bs>VTp4x|80<*MbQW(e!JmsDV}y2X$gTsoX_>$Ny^R(q0_E{@$F> z2cc&qhrUv~OxUY|ONRGJ(f3Cw-(;Ms!rs`fTo@|+aU93R=CYd>@>HArjHT0N1 zHQdfH#3OKB;6`cM`{AZu@+ZC7VIn*SWp*Tx8CpP;ofsFw(^ED@CN@ zaXS{%yD0VAJOx&^7o+@$gl!{wr)LT^4QLfKHs*Fo)r{WR?l!0Z`_B*t430(hu1H=4 z+TLMRQ}QX}%NP|^pA(vmvfxdRkFFNPaj~kkFjdOep%fVf*cPSp7}y8Go6~(*(F&pA z4bxhb(2A=ct~;}2oJ)-tG4clIih1GQv8S)otL$fldKi|V&FU`jqQ5W8uhwTDQo7k3 z$nX77k;FB5{o>>-AqChadqF>{KS(ol@=K2!U{Ymh#{771*47PWO2cg2>ZeTm5)`DW z4ZC#G-jnCe;%;cpp~0U(V*Q+3>7X^eI@nU{Jm%=Qw;gM{UEnjC`C%jbY|6GWnICjI zCbMcyFAn^C6feXFl0YG*uCp0>NlbNX52o(NL%*{xxm^ey13Q#xU|48wREF+&WWF44 zmF{Pv!saS5U2b7qwE>PuON#E>tR?w3z%Gj#ZWq+A+E0@E8_CEtriue@51@Hfcb2h%opjZ88S1cH(DopVx>Fn5NK zS2Apt(P$h4>HzIXQrFTqj&EZ4<02&#%u*dKqW!RR;OtpAtbVWVavSlL((V5m(D)yd{1Wl z!E2Ezjku@K-YVdwa^(lbYgEk-o`yZ&B*L%S>lNGaP@G=-Zp+4C`jn<^jnX4)f>(v5 z6>xbh>*wfyV|(OX*}U~#F1cWhK(PQfaX8?;8`)#YR9WAnZVKiLU0RMWwuVVdtVX#E z#xD!|LcW)J_1KoU=ygXEN-&2L#p4+Tx|nLxFod>k8k+yy3fnFGiK#HQur?21l17Jj zy~YZkHkA*5h04+&WQtD1{|s&}hXGzLC}DTCw=%8c_gxXST+8;F`T9!ql>0>N3O;^>-d z-qPC^h5=h_qJm#CxbjSGR&NfgAKp;sUhwRT4JF;!57Mb{&ravu@xqXZJfD14y@<$4 z*--4@MmUWDxr&_mP$dI^v2Ff@QKN$FSYozT36+5eGpk&u6RWdz4AQ<9gA4yNSvib< zhhs*m$;~wmV`o{(P9soUAX5-#by};U{LE?WBIH??kaxRPf|+MQ99B|o>LV;exF3ag z1KH{KA>wbI|6VRg0>hm5{T?3r509ne0AF4j`GTV&A)j1icDs(_m8mW6)&rXC-2~oI zNMrl?$bzTfc;uOU!nd0jm$HIke=w*C)l2#W& zsbdvVCb=h#yK`cIHn5K|W2?w)T_9_!8+LH;x8GqX000CY|f_CWW(S}Zu#8e35K1%l;{z9 ziar;jWEslUZ+9^0!I5hzQ>#G}S69efuPhy~YfD*Ftg#8YoQcVpQPdnJkhk#$%-Oy~ zs5f_F@=p#tMOQsiIn@Whj4)BDnr0R+C2%9#^O`J<210yjYBD=PA=__3lcn-v?$z+# z9iyG5Mu$mdp9}{?e5X~N8joTF^t%VDLn$=GRYxbsh69tK5_GAch1a~UG5?iANt{v! z%P1mQHO5jL5J@#ex;g604-r!Lb8R8arX!k*zHpilAL7ZTLh>7|AEcw71>lx+FF4p} z{iSp2@~JPLw3<%2;2EB3F*xg(6Lxet4kr*9e&;fwIe^CMI zDyV2A0616>3qFyxg5E7Feaco>lg)$z>vCG+!E3|+E{WAHq}I$u36z&lpdZXL2=23+ zD3c$MAsvd>|GHw=QL}cK8r?uVZx;ky@f%{eIhT{1+TMFh*`s_aEh-v|(dOUVS)^vy zLZeu2>2oe_j#8*I4Z+>oCzo;Ol7MaEp5UC8(D5Wzn^EnA_p!#xruQg>(jp8YZA=&9Q&yAZ{J!fU%OE=RnLv$v)3bJ?TFe? zTm;w8puHcm^rZc$&l{!h^I4a%BMeA^0q>pQnb43May;q!R z7IrBo4VgvNr=exL_~+8~$SMI7=Ca5#3>{1@xhyx0i&~gGRxcun8uvkYRh&0`^(I29!{K#rV$#ZL6sgkFm zcD(kss_GV6XqV;(oEQx_xgVFAq2G5rYb457>rhUJC@VvD0TQ|5*8!d|M_LsD3ar0t z{?xM)M5|tn!kZ!p(E)}~*dfLHtI6*x^={I)MN99an5Xwek!$5O9Bt!gtQMl1m>6D_ zHj;Vv2@=V^rvno%4eq;n6qkr!!xnr&8Y*2Nd(DyZ(p@z>krx}G zr&e3e_K1;>dOg)*=?7vaKZQR&T4yXx^-y)uakIQd9HFs9l3+9B+qZ21&!z+1HAn8V z7jQa367+}lFB-OWg!#-Uuo0(j9s>ERbR+An{_DiiJub?2oNpJ<+*VMF4a3 zGh1v{*T5jb?&^c+?c7nT;)-^MDI8a&yZ|Br5eh|K@fxfKcRkF-?PyNYWA?^Knx5EI zk#B(>#)=+D`G-HepK`=I>h+!_J2fXt)e59ik%HpKRG)b|CH0#+b=}0E-_~j^5jI7Qbz7)+h z$N49^f$hqx>-w}rPO~#s%Lg7Ba3YNj>Ub#anA3|37Bg@Cb=v+AFxl1v%1IsnmPY&+ zN|Qg<3;2%{m49Db6d)Kt0>A)ZJt}kn_SZSEMic)ogI(A{F3sPWWFAGB~nG0Ei8KM0)r^6#?A0hysYl_2j4& z3jqIe!2^p`fvQ4q>2g~5Nii2NN(>$-$^h3XB!Fv{Pyj*Fk4qYObO|>wLg5LQ9??zZ z3E4}zfvp-(IH`^f_b+7!PU<`@2OpgzgC~4Z#tjTKc|z+)l(u?0s^Jk2?cf`>Tu-mX zR89d@hCVuKKxSunLb*PW)8#QR!tpBvfPAh`_{Ih2F_#Md(isiDR*nONxIM`|j~No~ zPe@%U0F?81!uRg~nXK&jBu_nNsCzvjN0k6j_aoe_N*`$C{ira(*B=!NpC>f2M~6!x zxq2Mhm-}nXTYzK3{?Ny_D}%;!vFd9Pmw1JE>tZJ+|7o2Rx|zkM-0!c lCI$B7z`s`0Q$1a>$9`G&I0*jFLV5)JS2Y7hSK)tc^Dix5rEvfN delta 5364 zcmY+|bx>4M`#A7r>Fy4xC6-zm=|<_0?vRiW1VM5^8kW4IAPB2~bhmU$2}qZ;G!i2H z<>fc?&hNc{eV*sU%sDgXo_n9Z4RFpHxKa=kXE#|*To?=jea8c&FzMm(`7pBnBhfh@ zN52*iB1KsiO@SKzxgL$--;%PJ@u~%669$IcI~TPEvh2Vxs=LayOm4d#?kIy6gk2@E zxJXg;Hj)X#SPkXt;r&vP)ytTt=*0G>j|+uUDl|XgG*`y{yf*EVtYg7=sufjU0rwaG zvc0d{UtI_xdW8D;;5xV~M6JMs)5n5$P6p5N4IdQ&ONCwdr%}tG{`PsdSiB&^xxAjU zbVicKGKGU!6LaL$O7!%>T0LuTe5G_N=Z{!b$8fLTbPEQO_d_Wdm+Y3%zy^gF$uA*K z`m6!7{Z2D4!mkfbAp>HE?=btW4RX9T#vPinBwXO0K7wU3&%C?p!SMI4 z>l-zmsz|%+jBtDoDE{|Mb986nKi!p8->ruT%wGTcJaaW{Ep2Q6x-R)}=OqN&VM{TD zsJt|^rsEU^6n5+&i2UhYx8@eA%HX;U<%m>uFTGWoDg%W5_ zfCap+)SC0wKpNt~`5udw6ZquUn^%{llP$NX0{4UfksMjgJ$4Q$0g61UEDtUSWNQWj zk%IufLQ43)*@D}`0CgMR4Z`d=bFs@Meg7cyfJgD^Q{3dTkpP$Y+H9IV-|kwxSh9$ z$iK@%v>mz1qKP?4d7&5Jwr@3vpxx6QE9nd$2*vT^&cVqq2NdV|Q7RV}y zJ&N%a3H4vk&n_T0OQU2*3f!V0HBU)nsw6WDfVocxmWLDVR0-_e+`JKnlP(6rp((~W z29+nhn|5u#m9vfn=39p;VCqZPqshx10RGr%k__*bOsetLnElM_q07q~1>=>wh4V;( z3SnNuluL&>&Ltqp3EgO%JAy^U9H zM(PU(hm9B_QO9WxH>z;YDsq!?Eb4BwwGYN*)VHfJJj7NbOI1NJz~9V; z$dyLRhMxIGy%ZRmM-66}phjwAi*pKfUXh!3D6M+bmd{^SsgLQgyRHW6sPH!;93k*6 z;i^ezqnDGl&boZX#yE5(W4>FoTQv!DR0FGpCH~VX!!8yWGfqJw&mtA9JeC)m)m4m7 zYdVf+%3sbQ+ln#I%K8CG%Xp3YxFx*F`U-3qLw6{*#CpF3pZ`xm%9@4I$-z<%QT#;1 z&sj}QD?~+1!#B0f(J|u=D@T-KU{83uzFTaovWhsZ1qGvfg#O&VQCrbyVE>hk#eFD) zQxLzYQ_6_)lL>)aET5B~v3)KPLDEmqkk5_FI$SL(kXdcZHITK2DB}6mLsw(7krt(T+L**BOqzTR@{dEoD z4Vun?eVtL-Q9w!>m&RP%)S#UQHEU1_72TAk1@ebI1Xvgc_a#Y}w;o4im6m9>}yZ`v(Ni8e_|d|4ZoLBpQ| z;_`~0DSlLqSJLkKS|PM8M(lZ0!N$Za3;{S{)%WD zdsd0;%$}ps{RM|Qr?fI!{JR<$- z+m&*l{N`jOQy*s8k_{97<)(K7n}_c21NG)EqU zBdlfILr3}9@J_+|M1S`Y_8J!nvldN{=0=xBFEodPqPxE=M9bis_&L#aPu!j~jycR+ z^KGeP<0Y5Q!j)+Pq6DT;-Wm+ zapV(Hum8f~V$M3|w>5fI)wipNHA3#x-QShbSHED>?E7Zs^L_Q<^qq)BY(nVlJ4;-4 zKDYI$VLe`+)*-I5^)Vmh=#JBQU)%k$R(nx|)}C~v>MY#PC%^Mj3&6>)Y1@hvO5?W{ zQ|9g?2ARR^V1iNZnw&b~Xg4w_zl8aOVt6yiUk^1ojD7iTI^}aG){a*^=J`oy)9Giu zv)dsneMi)}S45yLVv!Wzh)Bn9!gr3bY5c@u)g5#0ZN!;F$Nc1RF|-eW9-9S7t9u$1 z8_R9GuEVd@n`u>YLO~&}*u_KK|1x;k?nm!*Qrhh|@9$1mlt8p1Q0Z@E-Mr+JEiPv0 zI++AD!GHDns%As{dAPeqA+VOoXY9(%>Zxogb3abZON1}R)jHnATt@9ED1ETdySi@3 zyI>pi)HGWO_~fE>pY~24Pfo60 zl7}pXT@#}2j|ME_&YEw}b)6d54vhz)S*G- z@_MP(OK}-m?z^|CX>(TLV;_V{#x&Ug@BQs*^EeZF#!{2;^qt!nd{he zDYshRl%C+Kh$V87h_b0u(ox93Y1&dJRkw!gfM*F~E<>-F!Lvhisr}oZk=8$FTDh$I zuqa6su^lnWJD$R@35mNkJ$T^!-mB$rIU_@K0{>nJ%RYy7e)b`e%oln`Y@-nZD^B&H zqfw+T=(WY!h$>>3qB~u%=@~8Fbw-Pe%7~Qoe%3!fR?f1~jv8KyW9z`}qbv=9-Qks} z3T!&B1)yc~+no9N=2>S~*AG~{@3LMJ3A|>TTK>~iomuIJnt#2rntO==x@oP)TbP8b`>tjcjD7`Ybc0y>{#m0;vGnW{J`Sba@# zUXGckQ5mE`Pl%7#-J&uiDRZmeSg7w3I*=~D&kvFvDLNtV)F?n&nE03p1YJw*TU*=- ze{63r-@)-j8F#&%Ttt>R`7QLwh@V}{0EBYnHEo>1p4jN;eIKGwKp-ff@{t3+X8VGy z)@h!Xj;+%>fkaQB?l~DI$IVsKlS*dqU(>S{mNMQ_rYTCJe}Czi0w+OyTX=|C)hzB! z%e{+2hfx?7Qqc|n^9|Mj>kWR(V0krSc^0784a6+W^I<(zv1DU{5 zffSs41a@W{h0AUtCS6&nEnsy=b;%88B1yNp?wYKY{t;i<>aQgg ziCaS1U&Aha1lOjoCQFXFCMIxjbav%)WvAo?u%|;!y4YZn;{@z~ta>tM(XaD^ys2~< zPK&tAO%1sUyZ-pR82_!iNwW900G|sxrF<>pWZY}^)qH#^bbSI!c$2GoTdL8N2liF2Dtv7$lFZ?IPk>ussiC#(Sx_|7{**h{Nb7W}K zw}!XdK_JlmJt|0B4HbQA8tWn$BaH4= z?}$Z}dDxwm>5LT}bs4Fj?i9}qockrmKYY;HvdEgao^2sTk^QTcv4wGL=wcP6)GDDC zGR#&aT&czhNPS7EO9tX6VM+3)sO!I}1H3wA;5N7pp6+rX?9Z%k#J` zip9DTi5t-VD%?i2ne2~>?_ADNBZY0!earQk!MmN?P><;ap~S3WEaO?8#&CT$*t~|K zaEI;r_YVdme;xS!Kh|N2uIM^a-!1qh$bUD(u(0gSw~?nEK$LfULfd-^RI9(hHfAwE=yFYKzo8YAZ*u>tF*pvVABizA z3pgSFAxE|RgCudA?x|~7&=l>UTxP6=abRXX`Nn&B6bMJH5Dv4S9kIF-A3^OPwz2A> zzB}Q3w@W?z3Zcgo~-qTy@oZSv3l5_V&KL3@WQB#0qW?Yrc)NOH?B z{i5z3w2{-oyg)etdEMP#*o*ia3j6SHbs&K)wlgtYl)LrT(OV)*24|=nd@mB;$NeFj zNh_ z7!D8!80CBdd@UyeWQu6Ozo7slFBLFTCTdZZK&4!~Cg1ut_ygv>zT1Lt@ET0RzF z_<`&q08 Date: Sat, 20 Apr 2019 13:17:54 +0200 Subject: [PATCH 086/223] fix tests --- .../Processing/Processors/Convolution/DetectEdgesTest.cs | 1 + .../Processing/Processors/Transforms/ResizeTests.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs index de72f6d09e..edb24d6f10 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs @@ -42,6 +42,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution var bounds = new Rectangle(10, 10, size.Width / 2, size.Height / 2); ctx.DetectEdges(bounds); }, + comparer: ValidatorComparer, useReferenceOutputFrom: nameof(this.DetectEdges_InBox)); } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs index 6bc2321de5..e3a43a652e 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs @@ -131,6 +131,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms testOutputDetails: workingBufferLimitInRows, appendPixelTypeToFileName: false); image.CompareToReferenceOutput( + ImageComparer.TolerantPercentage(0.001f), provider, testOutputDetails: workingBufferLimitInRows, appendPixelTypeToFileName: false); From d72db6dc7882a9f85c87d4a75091c1ab92262f9a Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 20 Apr 2019 13:21:15 +0200 Subject: [PATCH 087/223] fix license text in CopyBuffers benchmark --- tests/ImageSharp.Benchmarks/General/CopyBuffers.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/ImageSharp.Benchmarks/General/CopyBuffers.cs b/tests/ImageSharp.Benchmarks/General/CopyBuffers.cs index 117cdba418..4c6458c306 100644 --- a/tests/ImageSharp.Benchmarks/General/CopyBuffers.cs +++ b/tests/ImageSharp.Benchmarks/General/CopyBuffers.cs @@ -1,7 +1,5 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// using System; using System.Buffers; From b4744dd07f5e756e7cc4e78a0ce26744d2aed269 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 22 Apr 2019 13:38:01 +0200 Subject: [PATCH 088/223] update travis.yml --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2515ca82a9..551ea129bf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,10 +3,10 @@ solution: ImageSharp.sln matrix: include: - - os: linux # Ubuntu 14.04 - dist: trusty + - os: linux # Ubuntu 16.04 + dist: xenial sudo: required - dotnet: 2.1.401 + dotnet: 2.1.603 mono: latest # - os: osx # OSX 10.11 # osx_image: xcode7.3.1 @@ -40,4 +40,4 @@ addons: notification_email: james_south@hotmail.com build_command_prepend: "dotnet restore" build_command: "dotnet build -c Release" - branch_pattern: coverity_scan \ No newline at end of file +branch_pattern: coverity_scan \ No newline at end of file From ea74e91e006d4c0c0d549df7ce10fe7048026884 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 22 Apr 2019 13:44:10 +0200 Subject: [PATCH 089/223] I'm terrible copy-paster --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 551ea129bf..6fd38484dd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -40,4 +40,4 @@ addons: notification_email: james_south@hotmail.com build_command_prepend: "dotnet restore" build_command: "dotnet build -c Release" -branch_pattern: coverity_scan \ No newline at end of file + branch_pattern: coverity_scan \ No newline at end of file From bee3a67a71d92b3dfd4c6ad54f08d6cf5f5c84af Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 22 Apr 2019 14:22:04 +0200 Subject: [PATCH 090/223] extend the CopyBuffers benchmark --- .../General/CopyBuffers.cs | 199 +++++++++++------- 1 file changed, 124 insertions(+), 75 deletions(-) diff --git a/tests/ImageSharp.Benchmarks/General/CopyBuffers.cs b/tests/ImageSharp.Benchmarks/General/CopyBuffers.cs index 4c6458c306..32f1d10c79 100644 --- a/tests/ImageSharp.Benchmarks/General/CopyBuffers.cs +++ b/tests/ImageSharp.Benchmarks/General/CopyBuffers.cs @@ -88,12 +88,32 @@ namespace SixLabors.ImageSharp.Benchmarks.General this.sourceMemory.Span.CopyTo(this.destMemory.Span); } - [Benchmark(Description = "Unsafe.CopyBlock()")] - public unsafe void UnsafeCopyBlock() + [Benchmark(Description = "Unsafe.CopyBlock(ref)")] + public unsafe void UnsafeCopyBlockReferences() + { + Unsafe.CopyBlock(ref this.destArray[0], ref this.sourceArray[0], (uint)this.Count); + } + + [Benchmark(Description = "Unsafe.CopyBlock(ptr)")] + public unsafe void UnsafeCopyBlockPointers() + { + void* pinnedDestination = this.destHandle.Pointer; + void* pinnedSource = this.sourceHandle.Pointer; + Unsafe.CopyBlock(pinnedDestination, pinnedSource, (uint)this.Count); + } + + [Benchmark(Description = "Unsafe.CopyBlockUnaligned(ref)")] + public unsafe void UnsafeCopyBlockUnalignedReferences() + { + Unsafe.CopyBlockUnaligned(ref this.destArray[0], ref this.sourceArray[0], (uint)this.Count); + } + + [Benchmark(Description = "Unsafe.CopyBlockUnaligned(ptr)")] + public unsafe void UnsafeCopyBlockUnalignedPointers() { void* pinnedDestination = this.destHandle.Pointer; void* pinnedSource = this.sourceHandle.Pointer; - Unsafe.CopyBlock(pinnedSource, pinnedDestination, (uint)this.Count); + Unsafe.CopyBlockUnaligned(pinnedDestination, pinnedSource, (uint)this.Count); } // BenchmarkDotNet=v0.11.3, OS=Windows 10.0.17134.706 (1803/April2018Update/Redstone4) @@ -106,77 +126,106 @@ namespace SixLabors.ImageSharp.Benchmarks.General // // IterationCount=3 LaunchCount=1 WarmupCount=3 // - // Method | Job | Runtime | Count | Mean | Error | StdDev | Ratio | RatioSD | Gen 0/1k Op | Gen 1/1k Op | Gen 2/1k Op | Allocated Memory/Op | - // -------------------- |----- |-------- |------ |-----------:|-----------:|----------:|------:|--------:|------------:|------------:|------------:|--------------------:| - // Array.Copy() | Clr | Clr | 10 | 23.579 ns | 1.6836 ns | 0.0923 ns | 1.00 | 0.00 | - | - | - | - | - // Buffer.BlockCopy() | Clr | Clr | 10 | 11.796 ns | 0.5280 ns | 0.0289 ns | 0.50 | 0.00 | - | - | - | - | - // Buffer.MemoryCopy() | Clr | Clr | 10 | 3.206 ns | 8.1741 ns | 0.4480 ns | 0.14 | 0.02 | - | - | - | - | - // Marshal.Copy() | Clr | Clr | 10 | 15.577 ns | 2.0937 ns | 0.1148 ns | 0.66 | 0.00 | - | - | - | - | - // Span.CopyTo() | Clr | Clr | 10 | 32.287 ns | 2.4107 ns | 0.1321 ns | 1.37 | 0.01 | - | - | - | - | - // Unsafe.CopyBlock() | Clr | Clr | 10 | 3.266 ns | 0.3848 ns | 0.0211 ns | 0.14 | 0.00 | - | - | - | - | - // | | | | | | | | | | | | | - // Array.Copy() | Core | Core | 10 | 19.713 ns | 7.3026 ns | 0.4003 ns | 1.00 | 0.00 | - | - | - | - | - // Buffer.BlockCopy() | Core | Core | 10 | 7.332 ns | 0.5465 ns | 0.0300 ns | 0.37 | 0.01 | - | - | - | - | - // Buffer.MemoryCopy() | Core | Core | 10 | 2.476 ns | 0.3476 ns | 0.0191 ns | 0.13 | 0.00 | - | - | - | - | - // Marshal.Copy() | Core | Core | 10 | 15.575 ns | 0.1335 ns | 0.0073 ns | 0.79 | 0.02 | - | - | - | - | - // Span.CopyTo() | Core | Core | 10 | 25.321 ns | 2.3556 ns | 0.1291 ns | 1.28 | 0.02 | - | - | - | - | - // Unsafe.CopyBlock() | Core | Core | 10 | 2.204 ns | 0.1836 ns | 0.0101 ns | 0.11 | 0.00 | - | - | - | - | - // | | | | | | | | | | | | | - // Array.Copy() | Clr | Clr | 50 | 35.217 ns | 2.7642 ns | 0.1515 ns | 1.00 | 0.00 | - | - | - | - | - // Buffer.BlockCopy() | Clr | Clr | 50 | 24.224 ns | 0.8737 ns | 0.0479 ns | 0.69 | 0.00 | - | - | - | - | - // Buffer.MemoryCopy() | Clr | Clr | 50 | 3.827 ns | 4.8733 ns | 0.2671 ns | 0.11 | 0.01 | - | - | - | - | - // Marshal.Copy() | Clr | Clr | 50 | 28.103 ns | 1.3570 ns | 0.0744 ns | 0.80 | 0.00 | - | - | - | - | - // Span.CopyTo() | Clr | Clr | 50 | 34.137 ns | 2.9274 ns | 0.1605 ns | 0.97 | 0.01 | - | - | - | - | - // Unsafe.CopyBlock() | Clr | Clr | 50 | 4.999 ns | 0.1778 ns | 0.0097 ns | 0.14 | 0.00 | - | - | - | - | - // | | | | | | | | | | | | | - // Array.Copy() | Core | Core | 50 | 20.925 ns | 1.0219 ns | 0.0560 ns | 1.00 | 0.00 | - | - | - | - | - // Buffer.BlockCopy() | Core | Core | 50 | 8.083 ns | 0.2158 ns | 0.0118 ns | 0.39 | 0.00 | - | - | - | - | - // Buffer.MemoryCopy() | Core | Core | 50 | 2.919 ns | 0.2878 ns | 0.0158 ns | 0.14 | 0.00 | - | - | - | - | - // Marshal.Copy() | Core | Core | 50 | 16.663 ns | 0.2505 ns | 0.0137 ns | 0.80 | 0.00 | - | - | - | - | - // Span.CopyTo() | Core | Core | 50 | 26.940 ns | 11.5855 ns | 0.6350 ns | 1.29 | 0.03 | - | - | - | - | - // Unsafe.CopyBlock() | Core | Core | 50 | 1.940 ns | 0.6327 ns | 0.0347 ns | 0.09 | 0.00 | - | - | - | - | - // | | | | | | | | | | | | | - // Array.Copy() | Clr | Clr | 100 | 39.284 ns | 0.5647 ns | 0.0310 ns | 1.00 | 0.00 | - | - | - | - | - // Buffer.BlockCopy() | Clr | Clr | 100 | 28.930 ns | 0.6774 ns | 0.0371 ns | 0.74 | 0.00 | - | - | - | - | - // Buffer.MemoryCopy() | Clr | Clr | 100 | 5.859 ns | 2.7931 ns | 0.1531 ns | 0.15 | 0.00 | - | - | - | - | - // Marshal.Copy() | Clr | Clr | 100 | 36.529 ns | 0.9886 ns | 0.0542 ns | 0.93 | 0.00 | - | - | - | - | - // Span.CopyTo() | Clr | Clr | 100 | 36.152 ns | 1.5109 ns | 0.0828 ns | 0.92 | 0.00 | - | - | - | - | - // Unsafe.CopyBlock() | Clr | Clr | 100 | 9.317 ns | 0.4342 ns | 0.0238 ns | 0.24 | 0.00 | - | - | - | - | - // | | | | | | | | | | | | | - // Array.Copy() | Core | Core | 100 | 22.899 ns | 8.4066 ns | 0.4608 ns | 1.00 | 0.00 | - | - | - | - | - // Buffer.BlockCopy() | Core | Core | 100 | 10.696 ns | 0.8106 ns | 0.0444 ns | 0.47 | 0.01 | - | - | - | - | - // Buffer.MemoryCopy() | Core | Core | 100 | 4.102 ns | 0.9040 ns | 0.0496 ns | 0.18 | 0.01 | - | - | - | - | - // Marshal.Copy() | Core | Core | 100 | 17.917 ns | 2.6490 ns | 0.1452 ns | 0.78 | 0.01 | - | - | - | - | - // Span.CopyTo() | Core | Core | 100 | 28.247 ns | 0.6375 ns | 0.0349 ns | 1.23 | 0.03 | - | - | - | - | - // Unsafe.CopyBlock() | Core | Core | 100 | 3.611 ns | 0.4792 ns | 0.0263 ns | 0.16 | 0.00 | - | - | - | - | - // | | | | | | | | | | | | | - // Array.Copy() | Clr | Clr | 1000 | 48.907 ns | 4.4228 ns | 0.2424 ns | 1.00 | 0.00 | - | - | - | - | - // Buffer.BlockCopy() | Clr | Clr | 1000 | 40.653 ns | 1.4055 ns | 0.0770 ns | 0.83 | 0.01 | - | - | - | - | - // Buffer.MemoryCopy() | Clr | Clr | 1000 | 24.720 ns | 1.2651 ns | 0.0693 ns | 0.51 | 0.00 | - | - | - | - | - // Marshal.Copy() | Clr | Clr | 1000 | 42.336 ns | 2.2466 ns | 0.1231 ns | 0.87 | 0.00 | - | - | - | - | - // Span.CopyTo() | Clr | Clr | 1000 | 70.735 ns | 2.6215 ns | 0.1437 ns | 1.45 | 0.01 | - | - | - | - | - // Unsafe.CopyBlock() | Clr | Clr | 1000 | 44.520 ns | 0.9641 ns | 0.0528 ns | 0.91 | 0.00 | - | - | - | - | - // | | | | | | | | | | | | | - // Array.Copy() | Core | Core | 1000 | 46.286 ns | 11.6373 ns | 0.6379 ns | 1.00 | 0.00 | - | - | - | - | - // Buffer.BlockCopy() | Core | Core | 1000 | 34.243 ns | 7.2264 ns | 0.3961 ns | 0.74 | 0.01 | - | - | - | - | - // Buffer.MemoryCopy() | Core | Core | 1000 | 23.135 ns | 0.3153 ns | 0.0173 ns | 0.50 | 0.01 | - | - | - | - | - // Marshal.Copy() | Core | Core | 1000 | 46.219 ns | 1.2869 ns | 0.0705 ns | 1.00 | 0.01 | - | - | - | - | - // Span.CopyTo() | Core | Core | 1000 | 45.371 ns | 3.3581 ns | 0.1841 ns | 0.98 | 0.02 | - | - | - | - | - // Unsafe.CopyBlock() | Core | Core | 1000 | 29.347 ns | 1.1349 ns | 0.0622 ns | 0.63 | 0.01 | - | - | - | - | - // | | | | | | | | | | | | | - // Array.Copy() | Clr | Clr | 10000 | 218.445 ns | 9.2567 ns | 0.5074 ns | 1.00 | 0.00 | - | - | - | - | - // Buffer.BlockCopy() | Clr | Clr | 10000 | 209.610 ns | 6.7447 ns | 0.3697 ns | 0.96 | 0.00 | - | - | - | - | - // Buffer.MemoryCopy() | Clr | Clr | 10000 | 213.061 ns | 66.6490 ns | 3.6533 ns | 0.98 | 0.02 | - | - | - | - | - // Marshal.Copy() | Clr | Clr | 10000 | 214.426 ns | 27.7722 ns | 1.5223 ns | 0.98 | 0.00 | - | - | - | - | - // Span.CopyTo() | Clr | Clr | 10000 | 486.728 ns | 12.1537 ns | 0.6662 ns | 2.23 | 0.00 | - | - | - | - | - // Unsafe.CopyBlock() | Clr | Clr | 10000 | 452.973 ns | 25.1490 ns | 1.3785 ns | 2.07 | 0.00 | - | - | - | - | - // | | | | | | | | | | | | | - // Array.Copy() | Core | Core | 10000 | 203.365 ns | 3.2200 ns | 0.1765 ns | 1.00 | 0.00 | - | - | - | - | - // Buffer.BlockCopy() | Core | Core | 10000 | 193.319 ns | 8.3370 ns | 0.4570 ns | 0.95 | 0.00 | - | - | - | - | - // Buffer.MemoryCopy() | Core | Core | 10000 | 196.541 ns | 37.8056 ns | 2.0723 ns | 0.97 | 0.01 | - | - | - | - | - // Marshal.Copy() | Core | Core | 10000 | 206.454 ns | 3.7652 ns | 0.2064 ns | 1.02 | 0.00 | - | - | - | - | - // Span.CopyTo() | Core | Core | 10000 | 214.799 ns | 3.0667 ns | 0.1681 ns | 1.06 | 0.00 | - | - | - | - | - // Unsafe.CopyBlock() | Core | Core | 10000 | 134.428 ns | 2.6024 ns | 0.1426 ns | 0.66 | 0.00 | - | - | - | - | - + // | Method | Job | Runtime | Count | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | + // |------------------------------- |----- |-------- |------ |-----------:|-----------:|----------:|------:|--------:|------:|------:|------:|----------:| + // | Array.Copy() | Clr | Clr | 10 | 23.636 ns | 2.5299 ns | 0.1387 ns | 1.00 | 0.00 | - | - | - | - | + // | Buffer.BlockCopy() | Clr | Clr | 10 | 11.420 ns | 2.3341 ns | 0.1279 ns | 0.48 | 0.01 | - | - | - | - | + // | Buffer.MemoryCopy() | Clr | Clr | 10 | 2.861 ns | 0.5059 ns | 0.0277 ns | 0.12 | 0.00 | - | - | - | - | + // | Marshal.Copy() | Clr | Clr | 10 | 14.870 ns | 2.4541 ns | 0.1345 ns | 0.63 | 0.01 | - | - | - | - | + // | Span.CopyTo() | Clr | Clr | 10 | 31.906 ns | 1.2213 ns | 0.0669 ns | 1.35 | 0.01 | - | - | - | - | + // | Unsafe.CopyBlock(ref) | Clr | Clr | 10 | 3.513 ns | 0.7392 ns | 0.0405 ns | 0.15 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlock(ptr) | Clr | Clr | 10 | 3.053 ns | 0.2010 ns | 0.0110 ns | 0.13 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlockUnaligned(ref) | Clr | Clr | 10 | 3.497 ns | 0.4911 ns | 0.0269 ns | 0.15 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlockUnaligned(ptr) | Clr | Clr | 10 | 3.109 ns | 0.5958 ns | 0.0327 ns | 0.13 | 0.00 | - | - | - | - | + // | | | | | | | | | | | | | | + // | Array.Copy() | Core | Core | 10 | 19.709 ns | 2.1867 ns | 0.1199 ns | 1.00 | 0.00 | - | - | - | - | + // | Buffer.BlockCopy() | Core | Core | 10 | 7.377 ns | 1.1582 ns | 0.0635 ns | 0.37 | 0.01 | - | - | - | - | + // | Buffer.MemoryCopy() | Core | Core | 10 | 2.581 ns | 1.1607 ns | 0.0636 ns | 0.13 | 0.00 | - | - | - | - | + // | Marshal.Copy() | Core | Core | 10 | 15.197 ns | 2.8446 ns | 0.1559 ns | 0.77 | 0.01 | - | - | - | - | + // | Span.CopyTo() | Core | Core | 10 | 25.394 ns | 0.9782 ns | 0.0536 ns | 1.29 | 0.01 | - | - | - | - | + // | Unsafe.CopyBlock(ref) | Core | Core | 10 | 2.254 ns | 0.1590 ns | 0.0087 ns | 0.11 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlock(ptr) | Core | Core | 10 | 1.878 ns | 0.1035 ns | 0.0057 ns | 0.10 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlockUnaligned(ref) | Core | Core | 10 | 2.263 ns | 0.1383 ns | 0.0076 ns | 0.11 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlockUnaligned(ptr) | Core | Core | 10 | 1.877 ns | 0.0602 ns | 0.0033 ns | 0.10 | 0.00 | - | - | - | - | + // | | | | | | | | | | | | | | + // | Array.Copy() | Clr | Clr | 50 | 35.068 ns | 5.9137 ns | 0.3242 ns | 1.00 | 0.00 | - | - | - | - | + // | Buffer.BlockCopy() | Clr | Clr | 50 | 23.299 ns | 2.3797 ns | 0.1304 ns | 0.66 | 0.01 | - | - | - | - | + // | Buffer.MemoryCopy() | Clr | Clr | 50 | 3.598 ns | 4.8536 ns | 0.2660 ns | 0.10 | 0.01 | - | - | - | - | + // | Marshal.Copy() | Clr | Clr | 50 | 27.720 ns | 4.6602 ns | 0.2554 ns | 0.79 | 0.01 | - | - | - | - | + // | Span.CopyTo() | Clr | Clr | 50 | 35.673 ns | 16.2972 ns | 0.8933 ns | 1.02 | 0.03 | - | - | - | - | + // | Unsafe.CopyBlock(ref) | Clr | Clr | 50 | 5.534 ns | 2.8119 ns | 0.1541 ns | 0.16 | 0.01 | - | - | - | - | + // | Unsafe.CopyBlock(ptr) | Clr | Clr | 50 | 4.511 ns | 0.9555 ns | 0.0524 ns | 0.13 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlockUnaligned(ref) | Clr | Clr | 50 | 5.613 ns | 1.6679 ns | 0.0914 ns | 0.16 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlockUnaligned(ptr) | Clr | Clr | 50 | 4.884 ns | 7.3153 ns | 0.4010 ns | 0.14 | 0.01 | - | - | - | - | + // | | | | | | | | | | | | | | + // | Array.Copy() | Core | Core | 50 | 20.232 ns | 1.5720 ns | 0.0862 ns | 1.00 | 0.00 | - | - | - | - | + // | Buffer.BlockCopy() | Core | Core | 50 | 8.142 ns | 0.7860 ns | 0.0431 ns | 0.40 | 0.00 | - | - | - | - | + // | Buffer.MemoryCopy() | Core | Core | 50 | 2.962 ns | 0.0611 ns | 0.0033 ns | 0.15 | 0.00 | - | - | - | - | + // | Marshal.Copy() | Core | Core | 50 | 16.802 ns | 2.9686 ns | 0.1627 ns | 0.83 | 0.00 | - | - | - | - | + // | Span.CopyTo() | Core | Core | 50 | 26.571 ns | 0.9228 ns | 0.0506 ns | 1.31 | 0.01 | - | - | - | - | + // | Unsafe.CopyBlock(ref) | Core | Core | 50 | 2.219 ns | 0.7191 ns | 0.0394 ns | 0.11 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlock(ptr) | Core | Core | 50 | 1.751 ns | 0.1884 ns | 0.0103 ns | 0.09 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlockUnaligned(ref) | Core | Core | 50 | 2.177 ns | 0.4489 ns | 0.0246 ns | 0.11 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlockUnaligned(ptr) | Core | Core | 50 | 1.806 ns | 0.1063 ns | 0.0058 ns | 0.09 | 0.00 | - | - | - | - | + // | | | | | | | | | | | | | | + // | Array.Copy() | Clr | Clr | 100 | 39.158 ns | 4.3068 ns | 0.2361 ns | 1.00 | 0.00 | - | - | - | - | + // | Buffer.BlockCopy() | Clr | Clr | 100 | 27.623 ns | 0.4611 ns | 0.0253 ns | 0.71 | 0.00 | - | - | - | - | + // | Buffer.MemoryCopy() | Clr | Clr | 100 | 5.018 ns | 0.5689 ns | 0.0312 ns | 0.13 | 0.00 | - | - | - | - | + // | Marshal.Copy() | Clr | Clr | 100 | 33.527 ns | 1.9019 ns | 0.1042 ns | 0.86 | 0.01 | - | - | - | - | + // | Span.CopyTo() | Clr | Clr | 100 | 35.604 ns | 2.7039 ns | 0.1482 ns | 0.91 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlock(ref) | Clr | Clr | 100 | 7.853 ns | 0.4925 ns | 0.0270 ns | 0.20 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlock(ptr) | Clr | Clr | 100 | 7.406 ns | 1.9733 ns | 0.1082 ns | 0.19 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlockUnaligned(ref) | Clr | Clr | 100 | 7.822 ns | 0.6837 ns | 0.0375 ns | 0.20 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlockUnaligned(ptr) | Clr | Clr | 100 | 7.392 ns | 1.2832 ns | 0.0703 ns | 0.19 | 0.00 | - | - | - | - | + // | | | | | | | | | | | | | | + // | Array.Copy() | Core | Core | 100 | 22.909 ns | 2.9754 ns | 0.1631 ns | 1.00 | 0.00 | - | - | - | - | + // | Buffer.BlockCopy() | Core | Core | 100 | 10.687 ns | 1.1262 ns | 0.0617 ns | 0.47 | 0.00 | - | - | - | - | + // | Buffer.MemoryCopy() | Core | Core | 100 | 4.063 ns | 0.1607 ns | 0.0088 ns | 0.18 | 0.00 | - | - | - | - | + // | Marshal.Copy() | Core | Core | 100 | 18.067 ns | 4.0557 ns | 0.2223 ns | 0.79 | 0.01 | - | - | - | - | + // | Span.CopyTo() | Core | Core | 100 | 28.352 ns | 1.2762 ns | 0.0700 ns | 1.24 | 0.01 | - | - | - | - | + // | Unsafe.CopyBlock(ref) | Core | Core | 100 | 4.130 ns | 0.2013 ns | 0.0110 ns | 0.18 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlock(ptr) | Core | Core | 100 | 4.096 ns | 0.2460 ns | 0.0135 ns | 0.18 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlockUnaligned(ref) | Core | Core | 100 | 4.160 ns | 0.3174 ns | 0.0174 ns | 0.18 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlockUnaligned(ptr) | Core | Core | 100 | 3.480 ns | 1.1683 ns | 0.0640 ns | 0.15 | 0.00 | - | - | - | - | + // | | | | | | | | | | | | | | + // | Array.Copy() | Clr | Clr | 1000 | 49.059 ns | 2.0729 ns | 0.1136 ns | 1.00 | 0.00 | - | - | - | - | + // | Buffer.BlockCopy() | Clr | Clr | 1000 | 38.270 ns | 23.6970 ns | 1.2989 ns | 0.78 | 0.03 | - | - | - | - | + // | Buffer.MemoryCopy() | Clr | Clr | 1000 | 27.599 ns | 6.8328 ns | 0.3745 ns | 0.56 | 0.01 | - | - | - | - | + // | Marshal.Copy() | Clr | Clr | 1000 | 42.752 ns | 5.1357 ns | 0.2815 ns | 0.87 | 0.01 | - | - | - | - | + // | Span.CopyTo() | Clr | Clr | 1000 | 69.983 ns | 2.1860 ns | 0.1198 ns | 1.43 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlock(ref) | Clr | Clr | 1000 | 44.822 ns | 0.1625 ns | 0.0089 ns | 0.91 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlock(ptr) | Clr | Clr | 1000 | 45.072 ns | 1.4053 ns | 0.0770 ns | 0.92 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlockUnaligned(ref) | Clr | Clr | 1000 | 45.306 ns | 5.2646 ns | 0.2886 ns | 0.92 | 0.01 | - | - | - | - | + // | Unsafe.CopyBlockUnaligned(ptr) | Clr | Clr | 1000 | 44.813 ns | 0.9117 ns | 0.0500 ns | 0.91 | 0.00 | - | - | - | - | + // | | | | | | | | | | | | | | + // | Array.Copy() | Core | Core | 1000 | 51.907 ns | 3.1827 ns | 0.1745 ns | 1.00 | 0.00 | - | - | - | - | + // | Buffer.BlockCopy() | Core | Core | 1000 | 40.700 ns | 3.1488 ns | 0.1726 ns | 0.78 | 0.00 | - | - | - | - | + // | Buffer.MemoryCopy() | Core | Core | 1000 | 23.711 ns | 1.3004 ns | 0.0713 ns | 0.46 | 0.00 | - | - | - | - | + // | Marshal.Copy() | Core | Core | 1000 | 42.586 ns | 2.5390 ns | 0.1392 ns | 0.82 | 0.00 | - | - | - | - | + // | Span.CopyTo() | Core | Core | 1000 | 44.109 ns | 4.5604 ns | 0.2500 ns | 0.85 | 0.01 | - | - | - | - | + // | Unsafe.CopyBlock(ref) | Core | Core | 1000 | 33.926 ns | 5.1633 ns | 0.2830 ns | 0.65 | 0.01 | - | - | - | - | + // | Unsafe.CopyBlock(ptr) | Core | Core | 1000 | 33.267 ns | 0.2708 ns | 0.0148 ns | 0.64 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlockUnaligned(ref) | Core | Core | 1000 | 34.018 ns | 2.3238 ns | 0.1274 ns | 0.66 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlockUnaligned(ptr) | Core | Core | 1000 | 33.667 ns | 2.1983 ns | 0.1205 ns | 0.65 | 0.00 | - | - | - | - | + // | | | | | | | | | | | | | | + // | Array.Copy() | Clr | Clr | 10000 | 153.429 ns | 6.1735 ns | 0.3384 ns | 1.00 | 0.00 | - | - | - | - | + // | Buffer.BlockCopy() | Clr | Clr | 10000 | 201.373 ns | 4.3670 ns | 0.2394 ns | 1.31 | 0.00 | - | - | - | - | + // | Buffer.MemoryCopy() | Clr | Clr | 10000 | 211.768 ns | 71.3510 ns | 3.9110 ns | 1.38 | 0.02 | - | - | - | - | + // | Marshal.Copy() | Clr | Clr | 10000 | 215.299 ns | 17.2677 ns | 0.9465 ns | 1.40 | 0.01 | - | - | - | - | + // | Span.CopyTo() | Clr | Clr | 10000 | 486.325 ns | 32.4445 ns | 1.7784 ns | 3.17 | 0.01 | - | - | - | - | + // | Unsafe.CopyBlock(ref) | Clr | Clr | 10000 | 452.314 ns | 33.0593 ns | 1.8121 ns | 2.95 | 0.02 | - | - | - | - | + // | Unsafe.CopyBlock(ptr) | Clr | Clr | 10000 | 455.600 ns | 56.7534 ns | 3.1108 ns | 2.97 | 0.02 | - | - | - | - | + // | Unsafe.CopyBlockUnaligned(ref) | Clr | Clr | 10000 | 452.279 ns | 8.6457 ns | 0.4739 ns | 2.95 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlockUnaligned(ptr) | Clr | Clr | 10000 | 453.146 ns | 12.3776 ns | 0.6785 ns | 2.95 | 0.00 | - | - | - | - | + // | | | | | | | | | | | | | | + // | Array.Copy() | Core | Core | 10000 | 204.508 ns | 3.1652 ns | 0.1735 ns | 1.00 | 0.00 | - | - | - | - | + // | Buffer.BlockCopy() | Core | Core | 10000 | 193.345 ns | 1.3742 ns | 0.0753 ns | 0.95 | 0.00 | - | - | - | - | + // | Buffer.MemoryCopy() | Core | Core | 10000 | 196.978 ns | 18.3279 ns | 1.0046 ns | 0.96 | 0.01 | - | - | - | - | + // | Marshal.Copy() | Core | Core | 10000 | 206.878 ns | 6.9938 ns | 0.3834 ns | 1.01 | 0.00 | - | - | - | - | + // | Span.CopyTo() | Core | Core | 10000 | 215.733 ns | 15.4824 ns | 0.8486 ns | 1.05 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlock(ref) | Core | Core | 10000 | 186.894 ns | 8.7617 ns | 0.4803 ns | 0.91 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlock(ptr) | Core | Core | 10000 | 186.662 ns | 10.6059 ns | 0.5813 ns | 0.91 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlockUnaligned(ref) | Core | Core | 10000 | 187.489 ns | 13.1527 ns | 0.7209 ns | 0.92 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlockUnaligned(ptr) | Core | Core | 10000 | 186.586 ns | 4.6274 ns | 0.2536 ns | 0.91 | 0.00 | - | - | - | - | } } \ No newline at end of file From 04e607a651f2a535af8eb0d79f756b99fa79a129 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 22 Apr 2019 15:52:54 +0200 Subject: [PATCH 091/223] use HashCode.Combine() --- src/ImageSharp/Memory/RowInterval.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/ImageSharp/Memory/RowInterval.cs b/src/ImageSharp/Memory/RowInterval.cs index 3ee7ae774d..815918754a 100644 --- a/src/ImageSharp/Memory/RowInterval.cs +++ b/src/ImageSharp/Memory/RowInterval.cs @@ -65,12 +65,6 @@ namespace SixLabors.ImageSharp.Memory return !ReferenceEquals(null, obj) && obj is RowInterval other && this.Equals(other); } - public override int GetHashCode() - { - unchecked - { - return (this.Min * 397) ^ this.Max; - } - } + public override int GetHashCode() => HashCode.Combine(this.Min, this.Max); } } \ No newline at end of file From c6be724f7e2938fe2cc54940bce7fa5b04d40f7a Mon Sep 17 00:00:00 2001 From: Daniil Samoylov Date: Tue, 23 Apr 2019 16:26:17 +1200 Subject: [PATCH 092/223] Pass correct output size in ResizeMode.Min #892 (#893) --- .../Transforms/Resize/ResizeHelper.cs | 2 +- .../Transforms/ResizeHelperTests.cs | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs index 956e6b84e7..ae7b112fc1 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs @@ -296,7 +296,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms // Don't upscale if (width > sourceWidth || height > sourceHeight) { - return (new Size(sourceWidth, sourceWidth), new Rectangle(0, 0, sourceWidth, sourceHeight)); + return (new Size(sourceWidth, sourceHeight), new Rectangle(0, 0, sourceWidth, sourceHeight)); } // Find the shortest distance to go. diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeHelperTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeHelperTests.cs index b0d8ef6531..b5ed64f7ef 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeHelperTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeHelperTests.cs @@ -1,7 +1,9 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Transforms; +using SixLabors.Primitives; using Xunit; @@ -31,5 +33,24 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms int actualCount = ResizeHelper.CalculateResizeWorkerHeightInWindowBands(windowDiameter, width, sizeLimitHintInBytes); Assert.Equal(expectedCount, actualCount); } + + [Fact] + public void CalculateMinRectangleWhenSourceIsSmallerThanTarget() + { + var sourceSize = new Size(200, 100); + var target = new Size(400, 200); + + var actual = ResizeHelper.CalculateTargetLocationAndBounds( + sourceSize, + new ResizeOptions{ + Mode = ResizeMode.Min, + Size = target + }, + target.Width, + target.Height); + + Assert.Equal(sourceSize, actual.Item1); + Assert.Equal(new Rectangle(0, 0, sourceSize.Width, sourceSize.Height), actual.Item2); + } } } \ No newline at end of file From e59e5315a83baeebf4fbb68a0edc40d1dab1db9b Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 25 Apr 2019 23:29:03 +1000 Subject: [PATCH 093/223] Cleanup General Convolution (#887) * Remove multiple premultiplication. * Use in DenseMatrix everywhere. * Make private * Dont convert vector row on first pass * Remove incorrectly assigned alpha. * Remove boxing. * Use correct min row. * Reorder parameters * Correctly handle alpha component. * Update tests * Use dedicated methods over branching. --- .../Processing/PatternBrush{TPixel}.cs | 9 +- .../Common/Helpers/DenseMatrixUtils.cs | 240 +++++++++++++++--- .../Convolution/BoxBlurProcessor.cs | 3 +- .../Convolution/Convolution2DProcessor.cs | 48 +++- .../Convolution/Convolution2PassProcessor.cs | 53 +++- .../Convolution/ConvolutionProcessor.cs | 50 +++- .../Convolution/EdgeDetector2DProcessor.cs | 5 +- .../EdgeDetectorCompassProcessor.cs | 20 +- .../Convolution/EdgeDetectorProcessor.cs | 6 +- .../Convolution/GaussianBlurProcessor.cs | 2 +- .../Convolution/GaussianSharpenProcessor.cs | 2 +- .../Processors/Dithering/ErrorDiffuserBase.cs | 2 +- .../Samplers/GaussianBlur.cs | 19 ++ .../DataWriter/IccDataWriter.MatrixTests.cs | 2 +- .../Processors/Convolution/BoxBlurTest.cs | 2 +- .../Processors/Convolution/DetectEdgesTest.cs | 2 + 16 files changed, 378 insertions(+), 87 deletions(-) create mode 100644 tests/ImageSharp.Benchmarks/Samplers/GaussianBlur.cs diff --git a/src/ImageSharp.Drawing/Processing/PatternBrush{TPixel}.cs b/src/ImageSharp.Drawing/Processing/PatternBrush{TPixel}.cs index 46ed36f687..20161b517d 100644 --- a/src/ImageSharp.Drawing/Processing/PatternBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/PatternBrush{TPixel}.cs @@ -61,7 +61,7 @@ namespace SixLabors.ImageSharp.Processing /// Color of the fore. /// Color of the back. /// The pattern. - internal PatternBrush(TPixel foreColor, TPixel backColor, DenseMatrix pattern) + internal PatternBrush(TPixel foreColor, TPixel backColor, in DenseMatrix pattern) { var foreColorVector = foreColor.ToVector4(); var backColorVector = backColor.ToVector4(); @@ -93,10 +93,7 @@ namespace SixLabors.ImageSharp.Processing } /// - public BrushApplicator CreateApplicator(ImageFrame source, RectangleF region, GraphicsOptions options) - { - return new PatternBrushApplicator(source, this.pattern, this.patternVector, options); - } + public BrushApplicator CreateApplicator(ImageFrame source, RectangleF region, GraphicsOptions options) => new PatternBrushApplicator(source, this.pattern, this.patternVector, options); /// /// The pattern brush applicator. @@ -116,7 +113,7 @@ namespace SixLabors.ImageSharp.Processing /// The pattern. /// The patternVector. /// The options - public PatternBrushApplicator(ImageFrame source, DenseMatrix pattern, DenseMatrix patternVector, GraphicsOptions options) + public PatternBrushApplicator(ImageFrame source, in DenseMatrix pattern, DenseMatrix patternVector, GraphicsOptions options) : base(source, options) { this.pattern = pattern; diff --git a/src/ImageSharp/Common/Helpers/DenseMatrixUtils.cs b/src/ImageSharp/Common/Helpers/DenseMatrixUtils.cs index 2e700c9d67..427b240057 100644 --- a/src/ImageSharp/Common/Helpers/DenseMatrixUtils.cs +++ b/src/ImageSharp/Common/Helpers/DenseMatrixUtils.cs @@ -3,6 +3,7 @@ using System; using System.Numerics; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; @@ -11,83 +12,120 @@ namespace SixLabors.ImageSharp { /// /// Extension methods for . + /// TODO: One day rewrite all this to use SIMD intrinsics. There's a lot of scope for improvement. /// internal static class DenseMatrixUtils { /// - /// Computes the sum of vectors in weighted by the kernel weight values. + /// Computes the sum of vectors in the span referenced by weighted by the two kernel weight values. + /// Using this method the convolution filter is not applied to alpha in addition to the color channels. /// /// The pixel format. - /// The dense matrix. + /// The vertical dense matrix. + /// The horizontal dense matrix. /// The source frame. - /// The target row. + /// The target row base reference. /// The current row. /// The current column. + /// The minimum working area row. /// The maximum working area row. + /// The minimum working area column. /// The maximum working area column. - /// The column offset to apply to source sampling. - public static void Convolve( - in DenseMatrix matrix, + [MethodImpl(InliningOptions.ShortMethod)] + public static void Convolve2D3( + in DenseMatrix matrixY, + in DenseMatrix matrixX, Buffer2D sourcePixels, - Span targetRow, + ref Vector4 targetRowRef, int row, int column, + int minRow, int maxRow, - int maxColumn, - int offsetColumn) + int minColumn, + int maxColumn) where TPixel : struct, IPixel { Vector4 vector = default; - int matrixHeight = matrix.Rows; - int matrixWidth = matrix.Columns; - int radiusY = matrixHeight >> 1; - int radiusX = matrixWidth >> 1; - int sourceOffsetColumnBase = column + offsetColumn; - - for (int y = 0; y < matrixHeight; y++) - { - int offsetY = (row + y - radiusY).Clamp(0, maxRow); - Span sourceRowSpan = sourcePixels.GetRowSpan(offsetY); - for (int x = 0; x < matrixWidth; x++) - { - int offsetX = (sourceOffsetColumnBase + x - radiusX).Clamp(offsetColumn, maxColumn); - var currentColor = sourceRowSpan[offsetX].ToVector4(); - Vector4Utils.Premultiply(ref currentColor); - - vector += matrix[y, x] * currentColor; - } - } + Convolve2DImpl( + in matrixY, + in matrixX, + sourcePixels, + row, + column, + minRow, + maxRow, + minColumn, + maxColumn, + ref vector); - ref Vector4 target = ref targetRow[column]; + ref Vector4 target = ref Unsafe.Add(ref targetRowRef, column); vector.W = target.W; + Vector4Utils.UnPremultiply(ref vector); target = vector; } /// - /// Computes the sum of vectors in weighted by the two kernel weight values. + /// Computes the sum of vectors in the span referenced by weighted by the two kernel weight values. + /// Using this method the convolution filter is applied to alpha in addition to the color channels. /// /// The pixel format. /// The vertical dense matrix. /// The horizontal dense matrix. /// The source frame. - /// The target row. + /// The target row base reference. /// The current row. /// The current column. + /// The minimum working area row. /// The maximum working area row. + /// The minimum working area column. /// The maximum working area column. - /// The column offset to apply to source sampling. - public static void Convolve2D( + [MethodImpl(InliningOptions.ShortMethod)] + public static void Convolve2D4( in DenseMatrix matrixY, in DenseMatrix matrixX, Buffer2D sourcePixels, - Span targetRow, + ref Vector4 targetRowRef, int row, int column, + int minRow, int maxRow, + int minColumn, + int maxColumn) + where TPixel : struct, IPixel + { + Vector4 vector = default; + + Convolve2DImpl( + in matrixY, + in matrixX, + sourcePixels, + row, + column, + minRow, + maxRow, + minColumn, + maxColumn, + ref vector); + + ref Vector4 target = ref Unsafe.Add(ref targetRowRef, column); + Vector4Utils.UnPremultiply(ref vector); + target = vector; + } + + [MethodImpl(InliningOptions.ShortMethod)] + public static void Convolve2DImpl( + in DenseMatrix matrixY, + in DenseMatrix matrixX, + Buffer2D sourcePixels, + int row, + int column, + int minRow, + int maxRow, + int minColumn, int maxColumn, - int offsetColumn) + ref Vector4 vector) where TPixel : struct, IPixel { Vector4 vectorY = default; @@ -96,16 +134,16 @@ namespace SixLabors.ImageSharp int matrixWidth = matrixY.Columns; int radiusY = matrixHeight >> 1; int radiusX = matrixWidth >> 1; - int sourceOffsetColumnBase = column + offsetColumn; + int sourceOffsetColumnBase = column + minColumn; for (int y = 0; y < matrixHeight; y++) { - int offsetY = (row + y - radiusY).Clamp(0, maxRow); + int offsetY = (row + y - radiusY).Clamp(minRow, maxRow); Span sourceRowSpan = sourcePixels.GetRowSpan(offsetY); for (int x = 0; x < matrixWidth; x++) { - int offsetX = (sourceOffsetColumnBase + x - radiusX).Clamp(offsetColumn, maxColumn); + int offsetX = (sourceOffsetColumnBase + x - radiusX).Clamp(minColumn, maxColumn); var currentColor = sourceRowSpan[offsetX].ToVector4(); Vector4Utils.Premultiply(ref currentColor); @@ -114,11 +152,133 @@ namespace SixLabors.ImageSharp } } - var vector = Vector4.SquareRoot((vectorX * vectorX) + (vectorY * vectorY)); - ref Vector4 target = ref targetRow[column]; + vector = Vector4.SquareRoot((vectorX * vectorX) + (vectorY * vectorY)); + } + + /// + /// Computes the sum of vectors in the span referenced by weighted by the kernel weight values. + /// Using this method the convolution filter is not applied to alpha in addition to the color channels. + /// + /// The pixel format. + /// The dense matrix. + /// The source frame. + /// The target row base reference. + /// The current row. + /// The current column. + /// The minimum working area row. + /// The maximum working area row. + /// The minimum working area column. + /// The maximum working area column. + [MethodImpl(InliningOptions.ShortMethod)] + public static void Convolve3( + in DenseMatrix matrix, + Buffer2D sourcePixels, + ref Vector4 targetRowRef, + int row, + int column, + int minRow, + int maxRow, + int minColumn, + int maxColumn) + where TPixel : struct, IPixel + { + Vector4 vector = default; + + ConvolveImpl( + in matrix, + sourcePixels, + row, + column, + minRow, + maxRow, + minColumn, + maxColumn, + ref vector); + + ref Vector4 target = ref Unsafe.Add(ref targetRowRef, column); vector.W = target.W; + Vector4Utils.UnPremultiply(ref vector); target = vector; } + + /// + /// Computes the sum of vectors in the span referenced by weighted by the kernel weight values. + /// Using this method the convolution filter is applied to alpha in addition to the color channels. + /// + /// The pixel format. + /// The dense matrix. + /// The source frame. + /// The target row base reference. + /// The current row. + /// The current column. + /// The minimum working area row. + /// The maximum working area row. + /// The minimum working area column. + /// The maximum working area column. + [MethodImpl(InliningOptions.ShortMethod)] + public static void Convolve4( + in DenseMatrix matrix, + Buffer2D sourcePixels, + ref Vector4 targetRowRef, + int row, + int column, + int minRow, + int maxRow, + int minColumn, + int maxColumn) + where TPixel : struct, IPixel + { + Vector4 vector = default; + + ConvolveImpl( + in matrix, + sourcePixels, + row, + column, + minRow, + maxRow, + minColumn, + maxColumn, + ref vector); + + ref Vector4 target = ref Unsafe.Add(ref targetRowRef, column); + Vector4Utils.UnPremultiply(ref vector); + target = vector; + } + + [MethodImpl(InliningOptions.ShortMethod)] + private static void ConvolveImpl( + in DenseMatrix matrix, + Buffer2D sourcePixels, + int row, + int column, + int minRow, + int maxRow, + int minColumn, + int maxColumn, + ref Vector4 vector) + where TPixel : struct, IPixel + { + int matrixHeight = matrix.Rows; + int matrixWidth = matrix.Columns; + int radiusY = matrixHeight >> 1; + int radiusX = matrixWidth >> 1; + int sourceOffsetColumnBase = column + minColumn; + + for (int y = 0; y < matrixHeight; y++) + { + int offsetY = (row + y - radiusY).Clamp(minRow, maxRow); + Span sourceRowSpan = sourcePixels.GetRowSpan(offsetY); + + for (int x = 0; x < matrixWidth; x++) + { + int offsetX = (sourceOffsetColumnBase + x - radiusX).Clamp(minColumn, maxColumn); + var currentColor = sourceRowSpan[offsetX].ToVector4(); + Vector4Utils.Premultiply(ref currentColor); + vector += matrix[y, x] * currentColor; + } + } + } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs index 644d6c9e17..3d5bdc42a7 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs @@ -49,7 +49,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution public DenseMatrix KernelY { get; } /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) => new Convolution2PassProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle, configuration); + protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + => new Convolution2PassProcessor(this.KernelX, this.KernelY, false).Apply(source, sourceRectangle, configuration); /// /// Create a 1 dimensional Box kernel. diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs index 341a26ae84..633b50a9b7 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs @@ -3,6 +3,7 @@ using System; using System.Numerics; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; @@ -23,11 +24,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// The horizontal gradient operator. /// The vertical gradient operator. - public Convolution2DProcessor(DenseMatrix kernelX, DenseMatrix kernelY) + /// Whether the convolution filter is applied to alpha as well as the color channels. + public Convolution2DProcessor(in DenseMatrix kernelX, in DenseMatrix kernelY, bool preserveAlpha) { Guard.IsTrue(kernelX.Size.Equals(kernelY.Size), $"{nameof(kernelX)} {nameof(kernelY)}", "Kernel sizes must be the same."); this.KernelX = kernelX; this.KernelY = kernelY; + this.PreserveAlpha = preserveAlpha; } /// @@ -40,6 +43,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// public DenseMatrix KernelY { get; } + /// + /// Gets a value indicating whether the convolution filter is applied to alpha as well as the color channels. + /// + public bool PreserveAlpha { get; } + /// protected override void OnFrameApply( ImageFrame source, @@ -48,6 +56,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { DenseMatrix matrixY = this.KernelY; DenseMatrix matrixX = this.KernelX; + bool preserveAlpha = this.PreserveAlpha; var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); int startY = interest.Y; @@ -71,18 +80,49 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { Span vectorSpan = vectorBuffer.Span; int length = vectorSpan.Length; + ref Vector4 vectorSpanRef = ref MemoryMarshal.GetReference(vectorSpan); for (int y = rows.Min; y < rows.Max; y++) { Span targetRowSpan = targetPixels.GetRowSpan(y).Slice(startX); PixelOperations.Instance.ToVector4(configuration, targetRowSpan.Slice(0, length), vectorSpan); - for (int x = 0; x < width; x++) + if (preserveAlpha) + { + for (int x = 0; x < width; x++) + { + DenseMatrixUtils.Convolve2D3( + in matrixY, + in matrixX, + source.PixelBuffer, + ref vectorSpanRef, + y, + x, + startY, + maxY, + startX, + maxX); + } + } + else { - DenseMatrixUtils.Convolve2D(in matrixY, in matrixX, source.PixelBuffer, vectorSpan, y, x, maxY, maxX, startX); + for (int x = 0; x < width; x++) + { + DenseMatrixUtils.Convolve2D4( + in matrixY, + in matrixX, + source.PixelBuffer, + ref vectorSpanRef, + y, + x, + startY, + maxY, + startX, + maxX); + } } - PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan.Slice(0, length), targetRowSpan); + PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan, targetRowSpan); } }); diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs index 9de4673289..03268c9dda 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs @@ -3,7 +3,7 @@ using System; using System.Numerics; - +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; @@ -24,10 +24,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// The horizontal gradient operator. /// The vertical gradient operator. - public Convolution2PassProcessor(DenseMatrix kernelX, DenseMatrix kernelY) + /// Whether the convolution filter is applied to alpha as well as the color channels. + public Convolution2PassProcessor( + in DenseMatrix kernelX, + in DenseMatrix kernelY, + bool preserveAlpha) { this.KernelX = kernelX; this.KernelY = kernelY; + this.PreserveAlpha = preserveAlpha; } /// @@ -40,13 +45,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// public DenseMatrix KernelY { get; } + /// + /// Gets a value indicating whether the convolution filter is applied to alpha as well as the color channels. + /// + public bool PreserveAlpha { get; } + /// protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { using (Buffer2D firstPassPixels = configuration.MemoryAllocator.Allocate2D(source.Size())) { - source.CopyTo(firstPassPixels); - var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); this.ApplyConvolution(firstPassPixels, source.PixelBuffer, interest, this.KernelX, configuration); this.ApplyConvolution(source.PixelBuffer, firstPassPixels, interest, this.KernelY, configuration); @@ -72,6 +80,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution Configuration configuration) { DenseMatrix matrix = kernel; + bool preserveAlpha = this.PreserveAlpha; + int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; int startX = sourceRectangle.X; @@ -89,18 +99,47 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { Span vectorSpan = vectorBuffer.Span; int length = vectorSpan.Length; + ref Vector4 vectorSpanRef = ref MemoryMarshal.GetReference(vectorSpan); for (int y = rows.Min; y < rows.Max; y++) { Span targetRowSpan = targetPixels.GetRowSpan(y).Slice(startX); PixelOperations.Instance.ToVector4(configuration, targetRowSpan.Slice(0, length), vectorSpan); - for (int x = 0; x < width; x++) + if (preserveAlpha) + { + for (int x = 0; x < width; x++) + { + DenseMatrixUtils.Convolve3( + in matrix, + sourcePixels, + ref vectorSpanRef, + y, + x, + startY, + maxY, + startX, + maxX); + } + } + else { - DenseMatrixUtils.Convolve(in matrix, sourcePixels, vectorSpan, y, x, maxY, maxX, startX); + for (int x = 0; x < width; x++) + { + DenseMatrixUtils.Convolve4( + in matrix, + sourcePixels, + ref vectorSpanRef, + y, + x, + startY, + maxY, + startX, + maxX); + } } - PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan.Slice(0, length), targetRowSpan); + PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan, targetRowSpan); } }); } diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs index 7e003cb03e..6c3b9a46f5 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs @@ -3,6 +3,7 @@ using System; using System.Numerics; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; @@ -22,17 +23,29 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Initializes a new instance of the class. /// /// The 2d gradient operator. - public ConvolutionProcessor(DenseMatrix kernelXY) => this.KernelXY = kernelXY; + /// Whether the convolution filter is applied to alpha as well as the color channels. + public ConvolutionProcessor(in DenseMatrix kernelXY, bool preserveAlpha) + { + this.KernelXY = kernelXY; + this.PreserveAlpha = preserveAlpha; + } /// /// Gets the 2d gradient operator. /// public DenseMatrix KernelXY { get; } + /// + /// Gets a value indicating whether the convolution filter is applied to alpha as well as the color channels. + /// + public bool PreserveAlpha { get; } + /// protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { DenseMatrix matrix = this.KernelXY; + bool preserveAlpha = this.PreserveAlpha; + var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); int startY = interest.Y; int endY = interest.Bottom; @@ -55,18 +68,47 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { Span vectorSpan = vectorBuffer.Span; int length = vectorSpan.Length; + ref Vector4 vectorSpanRef = ref MemoryMarshal.GetReference(vectorSpan); for (int y = rows.Min; y < rows.Max; y++) { Span targetRowSpan = targetPixels.GetRowSpan(y).Slice(startX); PixelOperations.Instance.ToVector4(configuration, targetRowSpan.Slice(0, length), vectorSpan); - for (int x = 0; x < width; x++) + if (preserveAlpha) + { + for (int x = 0; x < width; x++) + { + DenseMatrixUtils.Convolve3( + in matrix, + source.PixelBuffer, + ref vectorSpanRef, + y, + x, + startY, + maxY, + startX, + maxX); + } + } + else { - DenseMatrixUtils.Convolve(in matrix, source.PixelBuffer, vectorSpan, y, x, maxY, maxX, startX); + for (int x = 0; x < width; x++) + { + DenseMatrixUtils.Convolve4( + in matrix, + source.PixelBuffer, + ref vectorSpanRef, + y, + x, + startY, + maxY, + startX, + maxX); + } } - PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan.Slice(0, length), targetRowSpan); + PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan, targetRowSpan); } }); diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs index 8927716492..d2e9630dc0 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs @@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// The horizontal gradient operator. /// The vertical gradient operator. /// Whether to convert the image to grayscale before performing edge detection. - protected EdgeDetector2DProcessor(DenseMatrix kernelX, DenseMatrix kernelY, bool grayscale) + protected EdgeDetector2DProcessor(in DenseMatrix kernelX, in DenseMatrix kernelY, bool grayscale) { Guard.IsTrue(kernelX.Size.Equals(kernelY.Size), $"{nameof(kernelX)} {nameof(kernelY)}", "Kernel sizes must be the same."); this.KernelX = kernelX; @@ -43,7 +43,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution public bool Grayscale { get; set; } /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) => new Convolution2DProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle, configuration); + protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + => new Convolution2DProcessor(this.KernelX, this.KernelY, true).Apply(source, sourceRectangle, configuration); /// protected override void BeforeFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs index 4165cf024e..73f92fae38 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs @@ -5,14 +5,12 @@ using System; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Threading.Tasks; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing.Processors.Filters; -using SixLabors.Memory; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution @@ -28,10 +26,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Initializes a new instance of the class. /// /// Whether to convert the image to grayscale before performing edge detection. - protected EdgeDetectorCompassProcessor(bool grayscale) - { - this.Grayscale = grayscale; - } + protected EdgeDetectorCompassProcessor(bool grayscale) => this.Grayscale = grayscale; /// /// Gets the North gradient operator @@ -104,7 +99,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution // we need a clean copy for each pass to start from using (ImageFrame cleanCopy = source.Clone()) { - new ConvolutionProcessor(kernels[0]).Apply(source, sourceRectangle, configuration); + new ConvolutionProcessor(kernels[0], true).Apply(source, sourceRectangle, configuration); if (kernels.Length == 1) { @@ -133,7 +128,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { using (ImageFrame pass = cleanCopy.Clone()) { - new ConvolutionProcessor(kernels[i]).Apply(pass, sourceRectangle, configuration); + new ConvolutionProcessor(kernels[i], true).Apply(pass, sourceRectangle, configuration); Buffer2D passPixels = pass.PixelBuffer; Buffer2D targetPixels = source.PixelBuffer; @@ -147,10 +142,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { int offsetY = y - shiftY; - ref TPixel passPixelsBase = - ref MemoryMarshal.GetReference(passPixels.GetRowSpan(offsetY)); - ref TPixel targetPixelsBase = - ref MemoryMarshal.GetReference(targetPixels.GetRowSpan(offsetY)); + ref TPixel passPixelsBase = ref MemoryMarshal.GetReference(passPixels.GetRowSpan(offsetY)); + ref TPixel targetPixelsBase = ref MemoryMarshal.GetReference(targetPixels.GetRowSpan(offsetY)); for (int x = minX; x < maxX; x++) { @@ -158,8 +151,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution // Grab the max components of the two pixels ref TPixel currentPassPixel = ref Unsafe.Add(ref passPixelsBase, offsetX); - ref TPixel currentTargetPixel = - ref Unsafe.Add(ref targetPixelsBase, offsetX); + ref TPixel currentTargetPixel = ref Unsafe.Add(ref targetPixelsBase, offsetX); var pixelValue = Vector4.Max( currentPassPixel.ToVector4(), diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor.cs index 9173bc229b..edc7ec4ccd 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor.cs @@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// The 2d gradient operator. /// Whether to convert the image to grayscale before performing edge detection. - protected EdgeDetectorProcessor(DenseMatrix kernelXY, bool grayscale) + protected EdgeDetectorProcessor(in DenseMatrix kernelXY, bool grayscale) { this.KernelXY = kernelXY; this.Grayscale = grayscale; @@ -45,8 +45,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - { - new ConvolutionProcessor(this.KernelXY).Apply(source, sourceRectangle, configuration); - } + => new ConvolutionProcessor(this.KernelXY, true).Apply(source, sourceRectangle, configuration); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs index b3bc15d391..0fc822d57c 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs @@ -77,7 +77,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - => new Convolution2PassProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle, configuration); + => new Convolution2PassProcessor(this.KernelX, this.KernelY, false).Apply(source, sourceRectangle, configuration); /// /// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs index 786bf7757a..001471720a 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs @@ -79,7 +79,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - => new Convolution2PassProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle, configuration); + => new Convolution2PassProcessor(this.KernelX, this.KernelY, false).Apply(source, sourceRectangle, configuration); /// /// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function diff --git a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffuserBase.cs b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffuserBase.cs index 642da2f001..abf5dce184 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffuserBase.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffuserBase.cs @@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// /// The dithering matrix. /// The divisor. - internal ErrorDiffuserBase(DenseMatrix matrix, byte divisor) + internal ErrorDiffuserBase(in DenseMatrix matrix, byte divisor) { Guard.MustBeGreaterThan(divisor, 0, nameof(divisor)); diff --git a/tests/ImageSharp.Benchmarks/Samplers/GaussianBlur.cs b/tests/ImageSharp.Benchmarks/Samplers/GaussianBlur.cs new file mode 100644 index 0000000000..47bd42a3c2 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Samplers/GaussianBlur.cs @@ -0,0 +1,19 @@ +using BenchmarkDotNet.Attributes; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; + +namespace SixLabors.ImageSharp.Benchmarks.Samplers +{ + [Config(typeof(Config.ShortClr))] + public class GaussianBlur + { + [Benchmark] + public void Blur() + { + using (var image = new Image(Configuration.Default, 400, 400, Rgba32.White)) + { + image.Mutate(c => c.GaussianBlur()); + } + } + } +} diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MatrixTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MatrixTests.cs index b43c17f791..0882222226 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MatrixTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MatrixTests.cs @@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp.Tests.Icc [Theory] [MemberData(nameof(IccTestDataMatrix.Matrix2D_DenseMatrixTestData), MemberType = typeof(IccTestDataMatrix))] - internal void WriteMatrix2D_DenseMatrix(byte[] expected, int xCount, int yCount, bool isSingle, DenseMatrix data) + internal void WriteMatrix2D_DenseMatrix(byte[] expected, int xCount, int yCount, bool isSingle, in DenseMatrix data) { IccDataWriter writer = CreateWriter(); diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BoxBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BoxBlurTest.cs index 0c40debad1..1f0d12cf7f 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BoxBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BoxBlurTest.cs @@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution where TPixel : struct, IPixel { using (Image source = provider.GetImage()) - using (var image = source.Clone()) + using (Image image = source.Clone()) { var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs index edb24d6f10..b6a7741b3c 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs @@ -12,6 +12,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution { public class DetectEdgesTest : FileTestBase { + // I think our comparison is not accurate enough (nor can be) for RgbaVector. + // The image pixels are identical according to BeyondCompare. private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.0456F); public static readonly string[] CommonTestImages = { TestImages.Png.Bike }; From b5ad111eac9106ed3c247a30ba43b4d44fbe52fc Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 26 Apr 2019 09:09:20 +1000 Subject: [PATCH 094/223] Faster Jpeg Huffman Decoding. (#894) * Read from underlying stream less often * Update benchmark dependencies * Experimental mango port Currently broken * Populate table, 64byte buffer Still broken. * Baseline, non RST works * 15/19 baseline tests pass now. * Optimize position change. * 18/19 pass * 19/19 baseline decoded * Can now decode all images. * Now faster and much cleaner. * Cleanup * Fix reader, update benchmarks * Update dependencies * Remove unused method * No need to clean initial buffer * Remove bounds check on ReadByte() * Refactor from feedback --- .../ImageSharp.Drawing.csproj | 2 +- .../Jpeg/Components/Decoder/FastACTable.cs | 61 - .../Components/Decoder/HuffmanScanBuffer.cs | 188 +++ .../Components/Decoder/HuffmanScanDecoder.cs | 694 +++++++++++ .../Jpeg/Components/Decoder/HuffmanTable.cs | 172 +-- .../Jpeg/Components/Decoder/ScanDecoder.cs | 1058 ----------------- src/ImageSharp/Formats/Jpeg/JpegConstants.cs | 31 +- .../Formats/Jpeg/JpegDecoderCore.cs | 10 +- .../Formats/Jpeg/JpegThrowHelper.cs | 3 - .../IO/DoubleBufferedStreamReader.cs | 115 +- src/ImageSharp/ImageSharp.csproj | 4 +- .../Codecs/Jpeg/DecodeJpegParseStreamOnly.cs | 17 + .../Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs | 25 +- .../Codecs/Jpeg/DoubleBufferedStreams.cs | 42 +- .../Codecs/MultiImageBenchmarkBase.cs | 2 +- .../ImageSharp.Benchmarks.csproj | 2 +- .../ImageSharp.Tests/ImageSharp.Tests.csproj | 4 +- 17 files changed, 1160 insertions(+), 1270 deletions(-) delete mode 100644 src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTable.cs create mode 100644 src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanBuffer.cs create mode 100644 src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs diff --git a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj index 638687ae55..6f5cabb09b 100644 --- a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj +++ b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj @@ -39,7 +39,7 @@ - + diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTable.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTable.cs deleted file mode 100644 index dc78a89ddb..0000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTable.cs +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder -{ - internal unsafe struct FastACTable - { - /// - /// Gets the lookahead array. - /// - public fixed short Lookahead[512]; - - /// - /// Derives a lookup table for fast AC entropy scan decoding. - /// This can happen multiple times during progressive decoding but always outside mcu loops. - /// - /// The AC Huffman table. - public void Derive(ref HuffmanTable huffmanTable) - { - const int FastBits = ScanDecoder.FastBits; - ref short fastACRef = ref this.Lookahead[0]; - ref byte huffmanLookaheadRef = ref huffmanTable.Lookahead[0]; - ref byte huffmanValuesRef = ref huffmanTable.Values[0]; - ref short huffmanSizesRef = ref huffmanTable.Sizes[0]; - - int i; - for (i = 0; i < (1 << FastBits); i++) - { - byte fast = Unsafe.Add(ref huffmanLookaheadRef, i); - Unsafe.Add(ref fastACRef, i) = 0; - - if (fast < byte.MaxValue) - { - int rs = Unsafe.Add(ref huffmanValuesRef, fast); - int run = (rs >> 4) & 15; - int magbits = rs & 15; - int len = Unsafe.Add(ref huffmanSizesRef, fast); - - if (magbits != 0 && len + magbits <= FastBits) - { - // Magnitude code followed by receive_extend code - int k = ((i << len) & ((1 << FastBits) - 1)) >> (FastBits - magbits); - int m = 1 << (magbits - 1); - if (k < m) - { - k += (int)((~0U << magbits) + 1); - } - - // If the result is small enough, we can fit it in fastAC table - if (k >= -128 && k <= 127) - { - Unsafe.Add(ref fastACRef, i) = (short)((k << 8) + (run << 4) + (len + magbits)); - } - } - } - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanBuffer.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanBuffer.cs new file mode 100644 index 0000000000..72bfa38646 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanBuffer.cs @@ -0,0 +1,188 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.IO; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder +{ + /// + /// Used to buffer and track the bits read from the Huffman entropy encoded data. + /// + internal struct HuffmanScanBuffer + { + private readonly DoubleBufferedStreamReader stream; + + // The entropy encoded code buffer. + private ulong data; + + // The number of valid bits left to read in the buffer. + private int remain; + + // Whether there is more data to pull from the stream for the current mcu. + private bool noMore; + + public HuffmanScanBuffer(DoubleBufferedStreamReader stream) + { + this.stream = stream; + this.data = 0ul; + this.remain = 0; + this.Marker = JpegConstants.Markers.XFF; + this.MarkerPosition = 0; + this.BadMarker = false; + this.noMore = false; + this.Eof = false; + } + + /// + /// Gets or sets the current, if any, marker in the input stream. + /// + public byte Marker { get; set; } + + /// + /// Gets or sets the opening position of an identified marker. + /// + public long MarkerPosition { get; set; } + + /// + /// Gets or sets a value indicating whether we have a bad marker, I.E. One that is not between RST0 and RST7 + /// + public bool BadMarker { get; set; } + + /// + /// Gets or sets a value indicating whether we have prematurely reached the end of the file. + /// + public bool Eof { get; set; } + + [MethodImpl(InliningOptions.ShortMethod)] + public void CheckBits() + { + if (this.remain < 16) + { + this.FillBuffer(); + } + } + + [MethodImpl(InliningOptions.ShortMethod)] + public void Reset() + { + this.data = 0ul; + this.remain = 0; + this.Marker = JpegConstants.Markers.XFF; + this.MarkerPosition = 0; + this.BadMarker = false; + this.noMore = false; + this.Eof = false; + } + + [MethodImpl(InliningOptions.ShortMethod)] + public bool HasRestart() + { + byte m = this.Marker; + return m >= JpegConstants.Markers.RST0 && m <= JpegConstants.Markers.RST7; + } + + [MethodImpl(InliningOptions.ShortMethod)] + public void FillBuffer() + { + // Attempt to load at least the minimum number of required bits into the buffer. + // We fail to do so only if we hit a marker or reach the end of the input stream. + this.remain += 48; + this.data = (this.data << 48) | this.GetBytes(); + } + + [MethodImpl(InliningOptions.ShortMethod)] + public unsafe int DecodeHuffman(ref HuffmanTable h) + { + this.CheckBits(); + int v = this.PeekBits(JpegConstants.Huffman.LookupBits); + int symbol = h.LookaheadValue[v]; + int size = h.LookaheadSize[v]; + + if (size == JpegConstants.Huffman.SlowBits) + { + ulong x = this.data << (JpegConstants.Huffman.RegisterSize - this.remain); + while (x > h.MaxCode[size]) + { + size++; + } + + v = (int)(x >> (JpegConstants.Huffman.RegisterSize - size)); + symbol = h.Values[h.ValOffset[size] + v]; + } + + this.remain -= size; + + return symbol; + } + + [MethodImpl(InliningOptions.ShortMethod)] + public int Receive(int nbits) + { + this.CheckBits(); + return Extend(this.GetBits(nbits), nbits); + } + + [MethodImpl(InliningOptions.ShortMethod)] + public int GetBits(int nbits) => (int)ExtractBits(this.data, this.remain -= nbits, nbits); + + [MethodImpl(InliningOptions.ShortMethod)] + public int PeekBits(int nbits) => (int)ExtractBits(this.data, this.remain - nbits, nbits); + + [MethodImpl(InliningOptions.ShortMethod)] + private static ulong ExtractBits(ulong value, int offset, int size) => (value >> offset) & (ulong)((1 << size) - 1); + + [MethodImpl(InliningOptions.ShortMethod)] + private static int Extend(int v, int nbits) => v - ((((v + v) >> nbits) - 1) & ((1 << nbits) - 1)); + + [MethodImpl(InliningOptions.ShortMethod)] + private ulong GetBytes() + { + ulong temp = 0; + for (int i = 0; i < 6; i++) + { + int b = this.noMore ? 0 : this.stream.ReadByte(); + + if (b == -1) + { + // We've encountered the end of the file stream which means there's no EOI marker in the image + // or the SOS marker has the wrong dimensions set. + this.Eof = true; + b = 0; + } + + // Found a marker. + if (b == JpegConstants.Markers.XFF) + { + this.MarkerPosition = this.stream.Position - 1; + int c = this.stream.ReadByte(); + while (c == JpegConstants.Markers.XFF) + { + c = this.stream.ReadByte(); + + if (c == -1) + { + this.Eof = true; + c = 0; + break; + } + } + + if (c != 0) + { + this.Marker = (byte)c; + this.noMore = true; + if (!this.HasRestart()) + { + this.BadMarker = true; + } + } + } + + temp = (temp << 8) | (ulong)(long)b; + } + + return temp; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs new file mode 100644 index 0000000000..76fea92976 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs @@ -0,0 +1,694 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using SixLabors.ImageSharp.IO; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder +{ + /// + /// Decodes the Huffman encoded spectral scan. + /// Originally ported from + /// with additional fixes for both performance and common encoding errors. + /// + internal class HuffmanScanDecoder + { + private readonly JpegFrame frame; + private readonly HuffmanTable[] dcHuffmanTables; + private readonly HuffmanTable[] acHuffmanTables; + private readonly DoubleBufferedStreamReader stream; + private readonly JpegComponent[] components; + + // The restart interval. + private readonly int restartInterval; + + // The number of interleaved components. + private readonly int componentsLength; + + // The spectral selection start. + private readonly int spectralStart; + + // The spectral selection end. + private readonly int spectralEnd; + + // The successive approximation high bit end. + private readonly int successiveHigh; + + // The successive approximation low bit end. + private readonly int successiveLow; + + // How many mcu's are left to do. + private int todo; + + // The End-Of-Block countdown for ending the sequence prematurely when the remaining coefficients are zero. + private int eobrun; + + // The unzig data. + private ZigZag dctZigZag; + + private HuffmanScanBuffer scanBuffer; + + /// + /// Initializes a new instance of the class. + /// + /// The input stream. + /// The image frame. + /// The DC Huffman tables. + /// The AC Huffman tables. + /// The length of the components. Different to the array length. + /// The reset interval. + /// The spectral selection start. + /// The spectral selection end. + /// The successive approximation bit high end. + /// The successive approximation bit low end. + public HuffmanScanDecoder( + DoubleBufferedStreamReader stream, + JpegFrame frame, + HuffmanTable[] dcHuffmanTables, + HuffmanTable[] acHuffmanTables, + int componentsLength, + int restartInterval, + int spectralStart, + int spectralEnd, + int successiveHigh, + int successiveLow) + { + this.dctZigZag = ZigZag.CreateUnzigTable(); + this.stream = stream; + this.scanBuffer = new HuffmanScanBuffer(stream); + this.frame = frame; + this.dcHuffmanTables = dcHuffmanTables; + this.acHuffmanTables = acHuffmanTables; + this.components = frame.Components; + this.componentsLength = componentsLength; + this.restartInterval = restartInterval; + this.todo = restartInterval; + this.spectralStart = spectralStart; + this.spectralEnd = spectralEnd; + this.successiveHigh = successiveHigh; + this.successiveLow = successiveLow; + } + + /// + /// Decodes the entropy coded data. + /// + public void ParseEntropyCodedData() + { + if (!this.frame.Progressive) + { + this.ParseBaselineData(); + } + else + { + this.ParseProgressiveData(); + } + + if (this.scanBuffer.BadMarker) + { + this.stream.Position = this.scanBuffer.MarkerPosition; + } + } + + private void ParseBaselineData() + { + if (this.componentsLength == 1) + { + this.ParseBaselineDataNonInterleaved(); + } + else + { + this.ParseBaselineDataInterleaved(); + } + } + + private unsafe void ParseBaselineDataInterleaved() + { + // Interleaved + int mcu = 0; + int mcusPerColumn = this.frame.McusPerColumn; + int mcusPerLine = this.frame.McusPerLine; + + // Pre-derive the huffman table to avoid in-loop checks. + for (int i = 0; i < this.componentsLength; i++) + { + int order = this.frame.ComponentOrder[i]; + JpegComponent component = this.components[order]; + + ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; + ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId]; + dcHuffmanTable.Configure(); + acHuffmanTable.Configure(); + } + + for (int j = 0; j < mcusPerColumn; j++) + { + for (int i = 0; i < mcusPerLine; i++) + { + // Scan an interleaved mcu... process components in order + int mcuRow = mcu / mcusPerLine; + int mcuCol = mcu % mcusPerLine; + for (int k = 0; k < this.componentsLength; k++) + { + int order = this.frame.ComponentOrder[k]; + JpegComponent component = this.components[order]; + + ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; + ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId]; + + int h = component.HorizontalSamplingFactor; + int v = component.VerticalSamplingFactor; + + // Scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (int y = 0; y < v; y++) + { + int blockRow = (mcuRow * v) + y; + Span blockSpan = component.SpectralBlocks.GetRowSpan(blockRow); + ref Block8x8 blockRef = ref MemoryMarshal.GetReference(blockSpan); + + for (int x = 0; x < h; x++) + { + int blockCol = (mcuCol * h) + x; + + this.DecodeBlockBaseline( + component, + ref Unsafe.Add(ref blockRef, blockCol), + ref dcHuffmanTable, + ref acHuffmanTable); + } + } + } + + // After all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + mcu++; + this.HandleRestart(); + } + } + } + + private unsafe void ParseBaselineDataNonInterleaved() + { + JpegComponent component = this.components[this.frame.ComponentOrder[0]]; + + int w = component.WidthInBlocks; + int h = component.HeightInBlocks; + + ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; + ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId]; + dcHuffmanTable.Configure(); + acHuffmanTable.Configure(); + + int mcu = 0; + for (int j = 0; j < h; j++) + { + Span blockSpan = component.SpectralBlocks.GetRowSpan(j); + ref Block8x8 blockRef = ref MemoryMarshal.GetReference(blockSpan); + + for (int i = 0; i < w; i++) + { + this.DecodeBlockBaseline( + component, + ref Unsafe.Add(ref blockRef, i), + ref dcHuffmanTable, + ref acHuffmanTable); + + // Every data block is an MCU, so countdown the restart interval + mcu++; + + this.HandleRestart(); + } + } + } + + private void CheckProgressiveData() + { + // Validate successive scan parameters. + // Logic has been adapted from libjpeg. + // See Table B.3 – Scan header parameter size and values. itu-t81.pdf + bool invalid = false; + if (this.spectralStart == 0) + { + if (this.spectralEnd != 0) + { + invalid = true; + } + } + else + { + // Need not check Ss/Se < 0 since they came from unsigned bytes. + if (this.spectralEnd < this.spectralStart || this.spectralEnd > 63) + { + invalid = true; + } + + // AC scans may have only one component. + if (this.componentsLength != 1) + { + invalid = true; + } + } + + if (this.successiveHigh != 0) + { + // Successive approximation refinement scan: must have Al = Ah-1. + if (this.successiveHigh - 1 != this.successiveLow) + { + invalid = true; + } + } + + // TODO: How does this affect 12bit jpegs. + // According to libjpeg the range covers 8bit only? + if (this.successiveLow > 13) + { + invalid = true; + } + + if (invalid) + { + JpegThrowHelper.ThrowBadProgressiveScan(this.spectralStart, this.spectralEnd, this.successiveHigh, this.successiveLow); + } + } + + private void ParseProgressiveData() + { + this.CheckProgressiveData(); + + if (this.componentsLength == 1) + { + this.ParseProgressiveDataNonInterleaved(); + } + else + { + this.ParseProgressiveDataInterleaved(); + } + } + + private void ParseProgressiveDataInterleaved() + { + // Interleaved + int mcu = 0; + int mcusPerColumn = this.frame.McusPerColumn; + int mcusPerLine = this.frame.McusPerLine; + + // Pre-derive the huffman table to avoid in-loop checks. + for (int k = 0; k < this.componentsLength; k++) + { + int order = this.frame.ComponentOrder[k]; + JpegComponent component = this.components[order]; + ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; + dcHuffmanTable.Configure(); + } + + for (int j = 0; j < mcusPerColumn; j++) + { + for (int i = 0; i < mcusPerLine; i++) + { + // Scan an interleaved mcu... process components in order + int mcuRow = mcu / mcusPerLine; + int mcuCol = mcu % mcusPerLine; + for (int k = 0; k < this.componentsLength; k++) + { + int order = this.frame.ComponentOrder[k]; + JpegComponent component = this.components[order]; + ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; + ref HuffmanScanBuffer buffer = ref this.scanBuffer; + + int h = component.HorizontalSamplingFactor; + int v = component.VerticalSamplingFactor; + + // Scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (int y = 0; y < v; y++) + { + int blockRow = (mcuRow * v) + y; + Span blockSpan = component.SpectralBlocks.GetRowSpan(blockRow); + ref Block8x8 blockRef = ref MemoryMarshal.GetReference(blockSpan); + + for (int x = 0; x < h; x++) + { + if (buffer.Eof) + { + return; + } + + int blockCol = (mcuCol * h) + x; + + this.DecodeBlockProgressiveDC( + component, + ref Unsafe.Add(ref blockRef, blockCol), + ref dcHuffmanTable); + } + } + } + + // After all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + mcu++; + this.HandleRestart(); + } + } + } + + private unsafe void ParseProgressiveDataNonInterleaved() + { + JpegComponent component = this.components[this.frame.ComponentOrder[0]]; + ref HuffmanScanBuffer buffer = ref this.scanBuffer; + + int w = component.WidthInBlocks; + int h = component.HeightInBlocks; + + if (this.spectralStart == 0) + { + ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; + dcHuffmanTable.Configure(); + + int mcu = 0; + for (int j = 0; j < h; j++) + { + Span blockSpan = component.SpectralBlocks.GetRowSpan(j); + ref Block8x8 blockRef = ref MemoryMarshal.GetReference(blockSpan); + + for (int i = 0; i < w; i++) + { + if (buffer.Eof) + { + return; + } + + this.DecodeBlockProgressiveDC( + component, + ref Unsafe.Add(ref blockRef, i), + ref dcHuffmanTable); + + // Every data block is an MCU, so countdown the restart interval + mcu++; + this.HandleRestart(); + } + } + } + else + { + ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId]; + acHuffmanTable.Configure(); + + int mcu = 0; + for (int j = 0; j < h; j++) + { + Span blockSpan = component.SpectralBlocks.GetRowSpan(j); + ref Block8x8 blockRef = ref MemoryMarshal.GetReference(blockSpan); + + for (int i = 0; i < w; i++) + { + if (buffer.Eof) + { + return; + } + + this.DecodeBlockProgressiveAC( + ref Unsafe.Add(ref blockRef, i), + ref acHuffmanTable); + + // Every data block is an MCU, so countdown the restart interval + mcu++; + this.HandleRestart(); + } + } + } + } + + private void DecodeBlockBaseline( + JpegComponent component, + ref Block8x8 block, + ref HuffmanTable dcTable, + ref HuffmanTable acTable) + { + ref short blockDataRef = ref Unsafe.As(ref block); + ref HuffmanScanBuffer buffer = ref this.scanBuffer; + ref ZigZag zigzag = ref this.dctZigZag; + + // DC + int t = buffer.DecodeHuffman(ref dcTable); + if (t != 0) + { + t = buffer.Receive(t); + } + + t += component.DcPredictor; + component.DcPredictor = t; + blockDataRef = (short)t; + + // AC + for (int i = 1; i < 64;) + { + int s = buffer.DecodeHuffman(ref acTable); + + int r = s >> 4; + s &= 15; + + if (s != 0) + { + i += r; + s = buffer.Receive(s); + Unsafe.Add(ref blockDataRef, zigzag[i++]) = (short)s; + } + else + { + if (r == 0) + { + break; + } + + i += 16; + } + } + } + + private void DecodeBlockProgressiveDC(JpegComponent component, ref Block8x8 block, ref HuffmanTable dcTable) + { + ref short blockDataRef = ref Unsafe.As(ref block); + ref HuffmanScanBuffer buffer = ref this.scanBuffer; + + if (this.successiveHigh == 0) + { + // First scan for DC coefficient, must be first + int s = buffer.DecodeHuffman(ref dcTable); + if (s != 0) + { + s = buffer.Receive(s); + } + + s += component.DcPredictor; + component.DcPredictor = s; + blockDataRef = (short)(s << this.successiveLow); + } + else + { + // Refinement scan for DC coefficient + buffer.CheckBits(); + blockDataRef |= (short)(buffer.GetBits(1) << this.successiveLow); + } + } + + private void DecodeBlockProgressiveAC(ref Block8x8 block, ref HuffmanTable acTable) + { + ref short blockDataRef = ref Unsafe.As(ref block); + if (this.successiveHigh == 0) + { + // MCU decoding for AC initial scan (either spectral selection, + // or first pass of successive approximation). + if (this.eobrun != 0) + { + --this.eobrun; + return; + } + + ref HuffmanScanBuffer buffer = ref this.scanBuffer; + ref ZigZag zigzag = ref this.dctZigZag; + int start = this.spectralStart; + int end = this.spectralEnd; + int low = this.successiveLow; + + for (int i = start; i <= end; ++i) + { + int s = buffer.DecodeHuffman(ref acTable); + int r = s >> 4; + s &= 15; + + i += r; + + if (s != 0) + { + s = buffer.Receive(s); + Unsafe.Add(ref blockDataRef, zigzag[i]) = (short)(s << low); + } + else + { + if (r != 15) + { + this.eobrun = 1 << r; + if (r != 0) + { + buffer.CheckBits(); + this.eobrun += buffer.GetBits(r); + } + + --this.eobrun; + break; + } + } + } + } + else + { + // Refinement scan for these AC coefficients + this.DecodeBlockProgressiveACRefined(ref blockDataRef, ref acTable); + } + } + + private void DecodeBlockProgressiveACRefined(ref short blockDataRef, ref HuffmanTable acTable) + { + // Refinement scan for these AC coefficients + ref HuffmanScanBuffer buffer = ref this.scanBuffer; + ref ZigZag zigzag = ref this.dctZigZag; + int start = this.spectralStart; + int end = this.spectralEnd; + + int p1 = 1 << this.successiveLow; + int m1 = (-1) << this.successiveLow; + + int k = start; + + if (this.eobrun == 0) + { + for (; k <= end; k++) + { + int s = buffer.DecodeHuffman(ref acTable); + int r = s >> 4; + s &= 15; + + if (s != 0) + { + buffer.CheckBits(); + if (buffer.GetBits(1) != 0) + { + s = p1; + } + else + { + s = m1; + } + } + else + { + if (r != 15) + { + this.eobrun = 1 << r; + + if (r != 0) + { + buffer.CheckBits(); + this.eobrun += buffer.GetBits(r); + } + + break; + } + } + + do + { + ref short coef = ref Unsafe.Add(ref blockDataRef, zigzag[k]); + if (coef != 0) + { + buffer.CheckBits(); + if (buffer.GetBits(1) != 0) + { + if ((coef & p1) == 0) + { + coef += (short)(coef >= 0 ? p1 : m1); + } + } + } + else + { + if (--r < 0) + { + break; + } + } + + k++; + } + while (k <= end); + + if ((s != 0) && (k < 64)) + { + Unsafe.Add(ref blockDataRef, zigzag[k]) = (short)s; + } + } + } + + if (this.eobrun > 0) + { + for (; k <= end; k++) + { + ref short coef = ref Unsafe.Add(ref blockDataRef, zigzag[k]); + + if (coef != 0) + { + buffer.CheckBits(); + if (buffer.GetBits(1) != 0) + { + if ((coef & p1) == 0) + { + coef += (short)(coef >= 0 ? p1 : m1); + } + } + } + } + + --this.eobrun; + } + } + + [MethodImpl(InliningOptions.ShortMethod)] + private void Reset() + { + for (int i = 0; i < this.components.Length; i++) + { + this.components[i].DcPredictor = 0; + } + + this.eobrun = 0; + this.scanBuffer.Reset(); + } + + [MethodImpl(InliningOptions.ShortMethod)] + private bool HandleRestart() + { + if (this.restartInterval > 0 && (--this.todo) == 0) + { + this.todo = this.restartInterval; + + if (this.scanBuffer.HasRestart()) + { + this.Reset(); + return true; + } + + if (this.scanBuffer.Marker != JpegConstants.Markers.XFF) + { + this.stream.Position = this.scanBuffer.MarkerPosition; + this.Reset(); + return true; + } + } + + return false; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs index 7e025e271e..21ed5018f3 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs @@ -2,53 +2,60 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Buffers; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; - -using SixLabors.ImageSharp.Memory; using SixLabors.Memory; namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { /// - /// Represents a Huffman Table + /// Represents a Huffman coding table containing basic coding data plus tables for accellerated computation. /// [StructLayout(LayoutKind.Sequential)] internal unsafe struct HuffmanTable { - private bool isDerived; - private readonly MemoryAllocator memoryAllocator; + private bool isConfigured; -#pragma warning disable IDE0044 // Add readonly modifier - private fixed byte codeLengths[17]; -#pragma warning restore IDE0044 // Add readonly modifier + /// + /// Derived from the DHT marker. Sizes[k] = # of symbols with codes of length k bits; Sizes[0] is unused. + /// + public fixed byte Sizes[17]; /// - /// Gets the max code array. + /// Derived from the DHT marker. Contains the symbols, in order of incremental code length. /// - public fixed uint MaxCode[18]; + public fixed byte Values[256]; /// - /// Gets the value offset array. + /// Contains the largest code of length k (0 if none). MaxCode[17] is a sentinel to + /// ensure terminates. /// - public fixed int ValOffset[18]; + public fixed ulong MaxCode[18]; /// - /// Gets the huffman value array. + /// Values[] offset for codes of length k ValOffset[k] = Values[] index of 1st symbol of code length + /// k, less the smallest code of length k; so given a code of length k, the corresponding symbol is + /// Values[code + ValOffset[k]]. /// - public fixed byte Values[256]; + public fixed int ValOffset[19]; /// - /// Gets the lookahead array. + /// Contains the length of bits for the given k value. /// - public fixed byte Lookahead[512]; + public fixed byte LookaheadSize[JpegConstants.Huffman.LookupSize]; /// - /// Gets the sizes array + /// Lookahead table: indexed by the next bits of + /// the input data stream. If the next Huffman code is no more + /// than bits long, we can obtain its length and + /// the corresponding symbol directly from this tables. + /// + /// The lower 8 bits of each table entry contain the number of + /// bits in the corresponding Huffman code, or + 1 + /// if too long. The next 8 bits of each entry contain the symbol. /// - public fixed short Sizes[257]; + public fixed byte LookaheadValue[JpegConstants.Huffman.LookupSize]; /// /// Initializes a new instance of the struct. @@ -58,91 +65,104 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// The huffman values public HuffmanTable(MemoryAllocator memoryAllocator, ReadOnlySpan codeLengths, ReadOnlySpan values) { - this.isDerived = false; + this.isConfigured = false; this.memoryAllocator = memoryAllocator; - Unsafe.CopyBlockUnaligned(ref this.codeLengths[0], ref MemoryMarshal.GetReference(codeLengths), (uint)codeLengths.Length); + Unsafe.CopyBlockUnaligned(ref this.Sizes[0], ref MemoryMarshal.GetReference(codeLengths), (uint)codeLengths.Length); Unsafe.CopyBlockUnaligned(ref this.Values[0], ref MemoryMarshal.GetReference(values), (uint)values.Length); } /// - /// Expands the HuffmanTable into its derived form. + /// Expands the HuffmanTable into its readable form. /// - public void Derive() + public void Configure() { - if (this.isDerived) + if (this.isConfigured) { return; } - const int Length = 257; - using (IMemoryOwner huffcode = this.memoryAllocator.Allocate(Length)) - { - ref short huffcodeRef = ref MemoryMarshal.GetReference(huffcode.GetSpan()); - ref byte codeLengthsRef = ref this.codeLengths[0]; + int p, si; + Span huffsize = stackalloc char[257]; + Span huffcode = stackalloc uint[257]; + uint code; - // Figure C.1: make table of Huffman code length for each symbol - ref short sizesRef = ref this.Sizes[0]; - short x = 0; - for (short i = 1; i < 17; i++) + // Figure C.1: make table of Huffman code length for each symbol + p = 0; + for (int l = 1; l <= 16; l++) + { + int i = this.Sizes[l]; + while (i-- != 0) { - byte length = Unsafe.Add(ref codeLengthsRef, i); - for (short j = 0; j < length; j++) - { - Unsafe.Add(ref sizesRef, x++) = i; - } + huffsize[p++] = (char)l; } + } - Unsafe.Add(ref sizesRef, x) = 0; - - // Figure C.2: generate the codes themselves - int si = 0; - ref int valOffsetRef = ref this.ValOffset[0]; - ref uint maxcodeRef = ref this.MaxCode[0]; + huffsize[p] = (char)0; - uint code = 0; - int k; - for (k = 1; k < 17; k++) + // Figure C.2: generate the codes themselves + code = 0; + si = huffsize[0]; + p = 0; + while (huffsize[p] != 0) + { + while (huffsize[p] == si) { - // Compute delta to add to code to compute symbol id. - Unsafe.Add(ref valOffsetRef, k) = (int)(si - code); - if (Unsafe.Add(ref sizesRef, si) == k) - { - while (Unsafe.Add(ref sizesRef, si) == k) - { - Unsafe.Add(ref huffcodeRef, si++) = (short)code++; - } - } + huffcode[p++] = code; + code++; + } + + code <<= 1; + si++; + } - // Figure F.15: generate decoding tables for bit-sequential decoding. - // Compute largest code + 1 for this size. preshifted as we need it later. - Unsafe.Add(ref maxcodeRef, k) = code << (16 - k); - code <<= 1; + // Figure F.15: generate decoding tables for bit-sequential decoding + p = 0; + for (int l = 1; l <= 16; l++) + { + if (this.Sizes[l] != 0) + { + int offset = p - (int)huffcode[p]; + this.ValOffset[l] = offset; + p += this.Sizes[l]; + this.MaxCode[l] = huffcode[p - 1]; // Maximum code of length l + this.MaxCode[l] <<= 64 - l; // Left justify + this.MaxCode[l] |= (1ul << (64 - l)) - 1; + } + else + { + this.MaxCode[l] = 0; } + } - Unsafe.Add(ref maxcodeRef, k) = 0xFFFFFFFF; + this.ValOffset[18] = 0; + this.MaxCode[17] = ulong.MaxValue; // Ensures huff decode terminates - // Generate non-spec lookup tables to speed up decoding. - const int FastBits = ScanDecoder.FastBits; - ref byte lookaheadRef = ref this.Lookahead[0]; - Unsafe.InitBlockUnaligned(ref lookaheadRef, 0xFF, 1 << FastBits); // Flag for non-accelerated + // Compute lookahead tables to speed up decoding. + // First we set all the table entries to JpegConstants.Huffman.SlowBits, indicating "too long"; + // then we iterate through the Huffman codes that are short enough and + // fill in all the entries that correspond to bit sequences starting + // with that code. + ref byte lookupSizeRef = ref this.LookaheadSize[0]; + Unsafe.InitBlockUnaligned(ref lookupSizeRef, JpegConstants.Huffman.SlowBits, JpegConstants.Huffman.LookupSize); - for (int i = 0; i < si; i++) + p = 0; + for (int length = 1; length <= JpegConstants.Huffman.LookupBits; length++) + { + for (int i = 1; i <= this.Sizes[length]; i++, p++) { - int size = Unsafe.Add(ref sizesRef, i); - if (size <= FastBits) + // length = current code's length, p = its index in huffcode[] & huffval[]. + // Generate left-justified code followed by all possible bit sequences + int lookbits = (int)(huffcode[p] << (JpegConstants.Huffman.LookupBits - length)); + for (int ctr = 1 << (JpegConstants.Huffman.LookupBits - length); ctr > 0; ctr--) { - int fastOffset = FastBits - size; - int fastCode = Unsafe.Add(ref huffcodeRef, i) << fastOffset; - int fastMax = 1 << fastOffset; - for (int left = 0; left < fastMax; left++) - { - Unsafe.Add(ref lookaheadRef, fastCode + left) = (byte)i; - } + this.LookaheadSize[lookbits] = (byte)length; + this.LookaheadValue[lookbits] = this.Values[p]; + lookbits++; } } } - this.isDerived = true; + this.isConfigured = true; } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs deleted file mode 100644 index ef4b359ff2..0000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs +++ /dev/null @@ -1,1058 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using SixLabors.ImageSharp.IO; -using SixLabors.ImageSharp.Memory; - -namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder -{ - /// - /// Decodes the Huffman encoded spectral scan. - /// Originally ported from - /// with additional fixes for both performance and common encoding errors. - /// - internal class ScanDecoder - { - // The number of bits that can be read via a LUT. - public const int FastBits = 9; - - // LUT mask for n rightmost bits. Bmask[n] = (1 << n) - 1 - private static readonly uint[] Bmask = { 0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, 16383, 32767, 65535 }; - - // LUT Bias[n] = (-1 << n) + 1 - private static readonly int[] Bias = { 0, -1, -3, -7, -15, -31, -63, -127, -255, -511, -1023, -2047, -4095, -8191, -16383, -32767 }; - - private readonly JpegFrame frame; - private readonly HuffmanTable[] dcHuffmanTables; - private readonly HuffmanTable[] acHuffmanTables; - private readonly FastACTable[] fastACTables; - - private readonly DoubleBufferedStreamReader stream; - private readonly JpegComponent[] components; - private readonly ZigZag dctZigZag; - - // The restart interval. - private readonly int restartInterval; - - // The number of interleaved components. - private readonly int componentsLength; - - // The spectral selection start. - private readonly int spectralStart; - - // The spectral selection end. - private readonly int spectralEnd; - - // The successive approximation high bit end. - private readonly int successiveHigh; - - // The successive approximation low bit end. - private readonly int successiveLow; - - // The number of valid bits left to read in the buffer. - private int codeBits; - - // The entropy encoded code buffer. - private uint codeBuffer; - - // Whether there is more data to pull from the stream for the current mcu. - private bool nomore; - - // Whether we have prematurely reached the end of the file. - private bool eof; - - // The current, if any, marker in the input stream. - private byte marker; - - // Whether we have a bad marker, I.E. One that is not between RST0 and RST7 - private bool badMarker; - - // The opening position of an identified marker. - private long markerPosition; - - // How many mcu's are left to do. - private int todo; - - // The End-Of-Block countdown for ending the sequence prematurely when the remaining coefficients are zero. - private int eobrun; - - /// - /// Initializes a new instance of the class. - /// - /// The input stream. - /// The image frame. - /// The DC Huffman tables. - /// The AC Huffman tables. - /// The fast AC decoding tables. - /// The length of the components. Different to the array length. - /// The reset interval. - /// The spectral selection start. - /// The spectral selection end. - /// The successive approximation bit high end. - /// The successive approximation bit low end. - public ScanDecoder( - DoubleBufferedStreamReader stream, - JpegFrame frame, - HuffmanTable[] dcHuffmanTables, - HuffmanTable[] acHuffmanTables, - FastACTable[] fastACTables, - int componentsLength, - int restartInterval, - int spectralStart, - int spectralEnd, - int successiveHigh, - int successiveLow) - { - this.dctZigZag = ZigZag.CreateUnzigTable(); - this.stream = stream; - this.frame = frame; - this.dcHuffmanTables = dcHuffmanTables; - this.acHuffmanTables = acHuffmanTables; - this.fastACTables = fastACTables; - this.components = frame.Components; - this.marker = JpegConstants.Markers.XFF; - this.markerPosition = 0; - this.componentsLength = componentsLength; - this.restartInterval = restartInterval; - this.spectralStart = spectralStart; - this.spectralEnd = spectralEnd; - this.successiveHigh = successiveHigh; - this.successiveLow = successiveLow; - } - - /// - /// Decodes the entropy coded data. - /// - public void ParseEntropyCodedData() - { - this.Reset(); - - if (!this.frame.Progressive) - { - this.ParseBaselineData(); - } - else - { - this.ParseProgressiveData(); - } - - if (this.badMarker) - { - this.stream.Position = this.markerPosition; - } - } - - [MethodImpl(InliningOptions.ShortMethod)] - private static uint LRot(uint x, int y) => (x << y) | (x >> (32 - y)); - - private void ParseBaselineData() - { - if (this.componentsLength == 1) - { - this.ParseBaselineDataNonInterleaved(); - } - else - { - this.ParseBaselineDataInterleaved(); - } - } - - private unsafe void ParseBaselineDataInterleaved() - { - // Interleaved - int mcu = 0; - int mcusPerColumn = this.frame.McusPerColumn; - int mcusPerLine = this.frame.McusPerLine; - - // Pre-derive the huffman table to avoid in-loop checks. - for (int i = 0; i < this.componentsLength; i++) - { - int order = this.frame.ComponentOrder[i]; - JpegComponent component = this.components[order]; - - ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; - ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId]; - dcHuffmanTable.Derive(); - acHuffmanTable.Derive(); - - ref FastACTable fastAcTable = ref this.fastACTables[component.ACHuffmanTableId]; - fastAcTable.Derive(ref acHuffmanTable); - } - - for (int j = 0; j < mcusPerColumn; j++) - { - for (int i = 0; i < mcusPerLine; i++) - { - // Scan an interleaved mcu... process components in order - int mcuRow = mcu / mcusPerLine; - int mcuCol = mcu % mcusPerLine; - for (int k = 0; k < this.componentsLength; k++) - { - int order = this.frame.ComponentOrder[k]; - JpegComponent component = this.components[order]; - - ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; - ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId]; - ref FastACTable fastAcTable = ref this.fastACTables[component.ACHuffmanTableId]; - ref short fastACRef = ref fastAcTable.Lookahead[0]; - - int h = component.HorizontalSamplingFactor; - int v = component.VerticalSamplingFactor; - - // Scan out an mcu's worth of this component; that's just determined - // by the basic H and V specified for the component - for (int y = 0; y < v; y++) - { - int blockRow = (mcuRow * v) + y; - Span blockSpan = component.SpectralBlocks.GetRowSpan(blockRow); - ref Block8x8 blockRef = ref MemoryMarshal.GetReference(blockSpan); - - for (int x = 0; x < h; x++) - { - if (this.eof) - { - return; - } - - int blockCol = (mcuCol * h) + x; - - this.DecodeBlockBaseline( - component, - ref Unsafe.Add(ref blockRef, blockCol), - ref dcHuffmanTable, - ref acHuffmanTable, - ref fastACRef); - } - } - } - - // After all interleaved components, that's an interleaved MCU, - // so now count down the restart interval - mcu++; - if (!this.ContinueOnMcuComplete()) - { - return; - } - } - } - } - - /// - /// Non-interleaved data, we just need to process one block at a time in trivial scanline order - /// number of blocks to do just depends on how many actual "pixels" each component has, - /// independent of interleaved MCU blocking and such. - /// - private unsafe void ParseBaselineDataNonInterleaved() - { - JpegComponent component = this.components[this.frame.ComponentOrder[0]]; - - int w = component.WidthInBlocks; - int h = component.HeightInBlocks; - - ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; - ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId]; - dcHuffmanTable.Derive(); - acHuffmanTable.Derive(); - - ref FastACTable fastAcTable = ref this.fastACTables[component.ACHuffmanTableId]; - fastAcTable.Derive(ref acHuffmanTable); - ref short fastACRef = ref fastAcTable.Lookahead[0]; - - int mcu = 0; - for (int j = 0; j < h; j++) - { - int blockRow = j; - Span blockSpan = component.SpectralBlocks.GetRowSpan(j); - ref Block8x8 blockRef = ref MemoryMarshal.GetReference(blockSpan); - - for (int i = 0; i < w; i++) - { - if (this.eof) - { - return; - } - - this.DecodeBlockBaseline( - component, - ref Unsafe.Add(ref blockRef, i), - ref dcHuffmanTable, - ref acHuffmanTable, - ref fastACRef); - - // Every data block is an MCU, so countdown the restart interval - mcu++; - if (!this.ContinueOnMcuComplete()) - { - return; - } - } - } - } - - private void CheckProgressiveData() - { - // Validate successive scan parameters. - // Logic has been adapted from libjpeg. - // See Table B.3 – Scan header parameter size and values. itu-t81.pdf - bool invalid = false; - if (this.spectralStart == 0) - { - if (this.spectralEnd != 0) - { - invalid = true; - } - } - else - { - // Need not check Ss/Se < 0 since they came from unsigned bytes. - if (this.spectralEnd < this.spectralStart || this.spectralEnd > 63) - { - invalid = true; - } - - // AC scans may have only one component. - if (this.componentsLength != 1) - { - invalid = true; - } - } - - if (this.successiveHigh != 0) - { - // Successive approximation refinement scan: must have Al = Ah-1. - if (this.successiveHigh - 1 != this.successiveLow) - { - invalid = true; - } - } - - // TODO: How does this affect 12bit jpegs. - // According to libjpeg the range covers 8bit only? - if (this.successiveLow > 13) - { - invalid = true; - } - - if (invalid) - { - JpegThrowHelper.ThrowBadProgressiveScan(this.spectralStart, this.spectralEnd, this.successiveHigh, this.successiveLow); - } - } - - private void ParseProgressiveData() - { - this.CheckProgressiveData(); - - if (this.componentsLength == 1) - { - this.ParseProgressiveDataNonInterleaved(); - } - else - { - this.ParseProgressiveDataInterleaved(); - } - } - - private void ParseProgressiveDataInterleaved() - { - // Interleaved - int mcu = 0; - int mcusPerColumn = this.frame.McusPerColumn; - int mcusPerLine = this.frame.McusPerLine; - - // Pre-derive the huffman table to avoid in-loop checks. - for (int k = 0; k < this.componentsLength; k++) - { - int order = this.frame.ComponentOrder[k]; - JpegComponent component = this.components[order]; - ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; - dcHuffmanTable.Derive(); - } - - for (int j = 0; j < mcusPerColumn; j++) - { - for (int i = 0; i < mcusPerLine; i++) - { - // Scan an interleaved mcu... process components in order - int mcuRow = mcu / mcusPerLine; - int mcuCol = mcu % mcusPerLine; - for (int k = 0; k < this.componentsLength; k++) - { - int order = this.frame.ComponentOrder[k]; - JpegComponent component = this.components[order]; - ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; - - int h = component.HorizontalSamplingFactor; - int v = component.VerticalSamplingFactor; - - // Scan out an mcu's worth of this component; that's just determined - // by the basic H and V specified for the component - for (int y = 0; y < v; y++) - { - int blockRow = (mcuRow * v) + y; - Span blockSpan = component.SpectralBlocks.GetRowSpan(blockRow); - ref Block8x8 blockRef = ref MemoryMarshal.GetReference(blockSpan); - - for (int x = 0; x < h; x++) - { - if (this.eof) - { - return; - } - - int blockCol = (mcuCol * h) + x; - - this.DecodeBlockProgressiveDC( - component, - ref Unsafe.Add(ref blockRef, blockCol), - ref dcHuffmanTable); - } - } - } - - // After all interleaved components, that's an interleaved MCU, - // so now count down the restart interval - mcu++; - if (!this.ContinueOnMcuComplete()) - { - return; - } - } - } - } - - /// - /// Non-interleaved data, we just need to process one block at a time, - /// in trivial scanline order - /// number of blocks to do just depends on how many actual "pixels" this - /// component has, independent of interleaved MCU blocking and such - /// - private unsafe void ParseProgressiveDataNonInterleaved() - { - JpegComponent component = this.components[this.frame.ComponentOrder[0]]; - - int w = component.WidthInBlocks; - int h = component.HeightInBlocks; - - if (this.spectralStart == 0) - { - ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; - dcHuffmanTable.Derive(); - - int mcu = 0; - for (int j = 0; j < h; j++) - { - Span blockSpan = component.SpectralBlocks.GetRowSpan(j); - ref Block8x8 blockRef = ref MemoryMarshal.GetReference(blockSpan); - - for (int i = 0; i < w; i++) - { - if (this.eof) - { - return; - } - - this.DecodeBlockProgressiveDC( - component, - ref Unsafe.Add(ref blockRef, i), - ref dcHuffmanTable); - - // Every data block is an MCU, so countdown the restart interval - mcu++; - if (!this.ContinueOnMcuComplete()) - { - return; - } - } - } - } - else - { - ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId]; - acHuffmanTable.Derive(); - - ref FastACTable fastAcTable = ref this.fastACTables[component.ACHuffmanTableId]; - fastAcTable.Derive(ref acHuffmanTable); - ref short fastACRef = ref fastAcTable.Lookahead[0]; - - int mcu = 0; - for (int j = 0; j < h; j++) - { - Span blockSpan = component.SpectralBlocks.GetRowSpan(j); - ref Block8x8 blockRef = ref MemoryMarshal.GetReference(blockSpan); - - for (int i = 0; i < w; i++) - { - if (this.eof) - { - return; - } - - this.DecodeBlockProgressiveAC( - ref Unsafe.Add(ref blockRef, i), - ref acHuffmanTable, - ref fastACRef); - - // Every data block is an MCU, so countdown the restart interval - mcu++; - if (!this.ContinueOnMcuComplete()) - { - return; - } - } - } - } - } - - private void DecodeBlockBaseline( - JpegComponent component, - ref Block8x8 block, - ref HuffmanTable dcTable, - ref HuffmanTable acTable, - ref short fastACRef) - { - this.CheckBits(); - int t = this.DecodeHuffman(ref dcTable); - - if (t < 0) - { - JpegThrowHelper.ThrowBadHuffmanCode(); - } - - ref short blockDataRef = ref Unsafe.As(ref block); - - int diff = t != 0 ? this.ExtendReceive(t) : 0; - int dc = component.DcPredictor + diff; - component.DcPredictor = dc; - blockDataRef = (short)dc; - - // Decode AC Components, See Jpeg Spec - int k = 1; - do - { - int zig; - int s; - - this.CheckBits(); - int c = this.PeekBits(); - int r = Unsafe.Add(ref fastACRef, c); - - if (r != 0) - { - // Fast AC path - k += (r >> 4) & 15; // Run - s = r & 15; // Combined Length - this.codeBuffer <<= s; - this.codeBits -= s; - - // Decode into unzigzag location - zig = this.dctZigZag[k++]; - Unsafe.Add(ref blockDataRef, zig) = (short)(r >> 8); - } - else - { - int rs = this.DecodeHuffman(ref acTable); - - if (rs < 0) - { - JpegThrowHelper.ThrowBadHuffmanCode(); - } - - s = rs & 15; - r = rs >> 4; - - if (s == 0) - { - if (rs != 0xF0) - { - break; // End block - } - - k += 16; - } - else - { - k += r; - - // Decode into unzigzag location - zig = this.dctZigZag[k++]; - Unsafe.Add(ref blockDataRef, zig) = (short)this.ExtendReceive(s); - } - } - } - while (k < 64); - } - - private void DecodeBlockProgressiveDC( - JpegComponent component, - ref Block8x8 block, - ref HuffmanTable dcTable) - { - this.CheckBits(); - - ref short blockDataRef = ref Unsafe.As(ref block); - - if (this.successiveHigh == 0) - { - // First scan for DC coefficient, must be first - int t = this.DecodeHuffman(ref dcTable); - int diff = t != 0 ? this.ExtendReceive(t) : 0; - - int dc = component.DcPredictor + diff; - component.DcPredictor = dc; - - blockDataRef = (short)(dc << this.successiveLow); - } - else - { - // Refinement scan for DC coefficient - if (this.GetBit() != 0) - { - blockDataRef += (short)(1 << this.successiveLow); - } - } - } - - private void DecodeBlockProgressiveAC( - ref Block8x8 block, - ref HuffmanTable acTable, - ref short fastACRef) - { - ref short blockDataRef = ref Unsafe.As(ref block); - - if (this.successiveHigh == 0) - { - // MCU decoding for AC initial scan (either spectral selection, - // or first pass of successive approximation). - int shift = this.successiveLow; - - if (this.eobrun != 0) - { - this.eobrun--; - return; - } - - int k = this.spectralStart; - do - { - int zig; - int s; - - this.CheckBits(); - int c = this.PeekBits(); - int r = Unsafe.Add(ref fastACRef, c); - - if (r != 0) - { - // Fast AC path - k += (r >> 4) & 15; // Run - s = r & 15; // Combined length - this.codeBuffer <<= s; - this.codeBits -= s; - - // Decode into unzigzag location - zig = this.dctZigZag[k++]; - Unsafe.Add(ref blockDataRef, zig) = (short)((r >> 8) << shift); - } - else - { - int rs = this.DecodeHuffman(ref acTable); - - if (rs < 0) - { - JpegThrowHelper.ThrowBadHuffmanCode(); - } - - s = rs & 15; - r = rs >> 4; - - if (s == 0) - { - if (r < 15) - { - this.eobrun = 1 << r; - if (r != 0) - { - this.eobrun += this.GetBits(r); - } - - this.eobrun--; - break; - } - - k += 16; - } - else - { - k += r; - zig = this.dctZigZag[k++]; - Unsafe.Add(ref blockDataRef, zig) = (short)(this.ExtendReceive(s) << shift); - } - } - } - while (k <= this.spectralEnd); - } - else - { - // Refinement scan for these AC coefficients - this.DecodeBlockProgressiveACRefined(ref blockDataRef, ref acTable); - } - } - - private void DecodeBlockProgressiveACRefined(ref short blockDataRef, ref HuffmanTable acTable) - { - int k; - - // Refinement scan for these AC coefficients - short bit = (short)(1 << this.successiveLow); - - if (this.eobrun != 0) - { - this.eobrun--; - for (k = this.spectralStart; k <= this.spectralEnd; k++) - { - ref short p = ref Unsafe.Add(ref blockDataRef, this.dctZigZag[k]); - if (p != 0) - { - if (this.GetBit() != 0) - { - if ((p & bit) == 0) - { - if (p > 0) - { - p += bit; - } - else - { - p -= bit; - } - } - } - } - } - } - else - { - k = this.spectralStart; - do - { - int rs = this.DecodeHuffman(ref acTable); - if (rs < 0) - { - JpegThrowHelper.ThrowBadHuffmanCode(); - } - - int s = rs & 15; - int r = rs >> 4; - - if (s == 0) - { - // r=15 s=0 should write 16 0s, so we just do - // a run of 15 0s and then write s (which is 0), - // so we don't have to do anything special here - if (r < 15) - { - this.eobrun = (1 << r) - 1; - - if (r != 0) - { - this.eobrun += this.GetBits(r); - } - - r = 64; // Force end of block - } - } - else - { - if (s != 1) - { - JpegThrowHelper.ThrowBadHuffmanCode(); - } - - // Sign bit - if (this.GetBit() != 0) - { - s = bit; - } - else - { - s = -bit; - } - } - - // Advance by r - while (k <= this.spectralEnd) - { - ref short p = ref Unsafe.Add(ref blockDataRef, this.dctZigZag[k++]); - if (p != 0) - { - if (this.GetBit() != 0) - { - if ((p & bit) == 0) - { - if (p > 0) - { - p += bit; - } - else - { - p -= bit; - } - } - } - } - else - { - if (r == 0) - { - p = (short)s; - break; - } - - r--; - } - } - } - while (k <= this.spectralEnd); - } - } - - [MethodImpl(InliningOptions.ShortMethod)] - private int GetBits(int n) - { - if (this.codeBits < n) - { - this.FillBuffer(); - } - - uint k = LRot(this.codeBuffer, n); - uint mask = Bmask[n]; - this.codeBuffer = k & ~mask; - k &= mask; - this.codeBits -= n; - return (int)k; - } - - [MethodImpl(InliningOptions.ShortMethod)] - private int GetBit() - { - if (this.codeBits < 1) - { - this.FillBuffer(); - } - - uint k = this.codeBuffer; - this.codeBuffer <<= 1; - this.codeBits--; - - return (int)(k & 0x80000000); - } - - [MethodImpl(InliningOptions.ColdPath)] - private void FillBuffer() - { - // Attempt to load at least the minimum number of required bits into the buffer. - // We fail to do so only if we hit a marker or reach the end of the input stream. - do - { - int b = this.nomore ? 0 : this.stream.ReadByte(); - - if (b == -1) - { - // We've encountered the end of the file stream which means there's no EOI marker in the image - // or the SOS marker has the wrong dimensions set. - this.eof = true; - b = 0; - } - - // Found a marker. - if (b == JpegConstants.Markers.XFF) - { - this.markerPosition = this.stream.Position - 1; - int c = this.stream.ReadByte(); - while (c == JpegConstants.Markers.XFF) - { - c = this.stream.ReadByte(); - - if (c == -1) - { - this.eof = true; - c = 0; - break; - } - } - - if (c != 0) - { - this.marker = (byte)c; - this.nomore = true; - if (!this.HasRestart()) - { - this.badMarker = true; - } - - return; - } - } - - this.codeBuffer |= (uint)b << (24 - this.codeBits); - this.codeBits += 8; - } - while (this.codeBits <= 24); - } - - [MethodImpl(InliningOptions.ShortMethod)] - private unsafe int DecodeHuffman(ref HuffmanTable table) - { - this.CheckBits(); - - // Look at the top FastBits and determine what symbol ID it is, - // if the code is <= FastBits. - int c = this.PeekBits(); - int k = table.Lookahead[c]; - if (k < 0xFF) - { - int s = table.Sizes[k]; - if (s > this.codeBits) - { - return -1; - } - - this.codeBuffer <<= s; - this.codeBits -= s; - return table.Values[k]; - } - - return this.DecodeHuffmanSlow(ref table); - } - - [MethodImpl(InliningOptions.ColdPath)] - private unsafe int DecodeHuffmanSlow(ref HuffmanTable table) - { - // Naive test is to shift the code_buffer down so k bits are - // valid, then test against MaxCode. To speed this up, we've - // preshifted maxcode left so that it has (16-k) 0s at the - // end; in other words, regardless of the number of bits, it - // wants to be compared against something shifted to have 16; - // that way we don't need to shift inside the loop. - uint temp = this.codeBuffer >> 16; - int k; - for (k = FastBits + 1; ; ++k) - { - if (temp < table.MaxCode[k]) - { - break; - } - } - - if (k == 17) - { - // Error! code not found - this.codeBits -= 16; - return -1; - } - - if (k > this.codeBits) - { - return -1; - } - - // Convert the huffman code to the symbol id - int c = (int)(((this.codeBuffer >> (32 - k)) & Bmask[k]) + table.ValOffset[k]); - - // Convert the id to a symbol - this.codeBits -= k; - this.codeBuffer <<= k; - return table.Values[c]; - } - - [MethodImpl(InliningOptions.ShortMethod)] - private int ExtendReceive(int n) - { - if (this.codeBits < n) - { - this.FillBuffer(); - } - - int sgn = (int)this.codeBuffer >> 31; - uint k = LRot(this.codeBuffer, n); - this.codeBuffer = k & ~Bmask[n]; - k &= Bmask[n]; - this.codeBits -= n; - return (int)(k + (Bias[n] & ~sgn)); - } - - [MethodImpl(InliningOptions.ShortMethod)] - private void CheckBits() - { - if (this.codeBits < 16) - { - this.FillBuffer(); - } - } - - [MethodImpl(InliningOptions.ShortMethod)] - private int PeekBits() => (int)((this.codeBuffer >> (32 - FastBits)) & ((1 << FastBits) - 1)); - - [MethodImpl(InliningOptions.ShortMethod)] - private bool ContinueOnMcuComplete() - { - if (--this.todo > 0) - { - return true; - } - - if (this.codeBits < 24) - { - this.FillBuffer(); - } - - // If it's NOT a restart, then just bail, so we get corrupt data rather than no data. - // Reset the stream to before any bad markers to ensure we can read successive segments. - if (this.badMarker) - { - this.stream.Position = this.markerPosition; - } - - if (!this.HasRestart()) - { - return false; - } - - this.Reset(); - - return true; - } - - [MethodImpl(InliningOptions.ShortMethod)] - private bool HasRestart() - { - byte m = this.marker; - return m >= JpegConstants.Markers.RST0 && m <= JpegConstants.Markers.RST7; - } - - private void Reset() - { - this.codeBits = 0; - this.codeBuffer = 0; - - for (int i = 0; i < this.components.Length; i++) - { - JpegComponent c = this.components[i]; - c.DcPredictor = 0; - } - - this.nomore = false; - this.marker = JpegConstants.Markers.XFF; - this.markerPosition = 0; - this.badMarker = false; - this.eobrun = 0; - - // No more than 1<<31 MCUs if no restartInterval? that's plenty safe since we don't even allow 1<<30 pixels - this.todo = this.restartInterval > 0 ? this.restartInterval : int.MaxValue; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/JpegConstants.cs b/src/ImageSharp/Formats/Jpeg/JpegConstants.cs index 49e3b41704..a39480e126 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegConstants.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegConstants.cs @@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg public static readonly IEnumerable FileExtensions = new[] { "jpg", "jpeg", "jfif" }; /// - /// Contains marker specific constants + /// Contains marker specific constants. /// // ReSharper disable InconsistentNaming internal static class Markers @@ -219,7 +219,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg } /// - /// Contains Adobe specific constants + /// Contains Adobe specific constants. /// internal static class Adobe { @@ -238,5 +238,32 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// public const byte ColorTransformYcck = 2; } + + /// + /// Contains Huffman specific constants. + /// + internal static class Huffman + { + /// + /// The size of the huffman decoder register. + /// + public const int RegisterSize = 64; + + /// + /// If the next Huffman code is no more than this number of bits, we can obtain its length + /// and the corresponding symbol directly from this tables. + /// + public const int LookupBits = 8; + + /// + /// If a Huffman code is this number of bits we cannot use the lookup table to determine its value. + /// + public const int SlowBits = LookupBits + 1; + + /// + /// The size of the lookup table. + /// + public const int LookupSize = 1 << LookupBits; + } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 6d6983f191..8bd6514e04 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -59,11 +59,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// private HuffmanTable[] acHuffmanTables; - /// - /// The fast AC tables used for entropy decoding - /// - private FastACTable[] fastACTables; - /// /// The reset interval determined by RST markers /// @@ -269,7 +264,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg const int maxTables = 4; this.dcHuffmanTables = new HuffmanTable[maxTables]; this.acHuffmanTables = new HuffmanTable[maxTables]; - this.fastACTables = new FastACTable[maxTables]; } // Break only when we discover a valid EOI marker. @@ -385,7 +379,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg this.Frame = null; this.dcHuffmanTables = null; this.acHuffmanTables = null; - this.fastACTables = null; } /// @@ -936,12 +929,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg int spectralEnd = this.temp[1]; int successiveApproximation = this.temp[2]; - var sd = new ScanDecoder( + var sd = new HuffmanScanDecoder( this.InputStream, this.Frame, this.dcHuffmanTables, this.acHuffmanTables, - this.fastACTables, selectorsCount, this.resetInterval, spectralStart, diff --git a/src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs b/src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs index b30da28c45..7e8384dcff 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs @@ -20,9 +20,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg [MethodImpl(InliningOptions.ColdPath)] public static void ThrowBadQuantizationTable() => throw new ImageFormatException("Bad Quantization Table index."); - [MethodImpl(InliningOptions.ColdPath)] - public static void ThrowBadHuffmanCode() => throw new ImageFormatException("Bad Huffman code."); - [MethodImpl(InliningOptions.ColdPath)] public static void ThrowBadSampling() => throw new ImageFormatException("Bad sampling factor."); diff --git a/src/ImageSharp/IO/DoubleBufferedStreamReader.cs b/src/ImageSharp/IO/DoubleBufferedStreamReader.cs index 6d5673d5a4..07f8928068 100644 --- a/src/ImageSharp/IO/DoubleBufferedStreamReader.cs +++ b/src/ImageSharp/IO/DoubleBufferedStreamReader.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.IO; using System.Runtime.CompilerServices; @@ -13,24 +14,28 @@ namespace SixLabors.ImageSharp.IO /// A stream reader that add a secondary level buffer in addition to native stream buffered reading /// to reduce the overhead of small incremental reads. /// - internal sealed class DoubleBufferedStreamReader : IDisposable + internal sealed unsafe class DoubleBufferedStreamReader : IDisposable { /// /// The length, in bytes, of the buffering chunk. /// - public const int ChunkLength = 4096; + public const int ChunkLength = 8192; - private const int ChunkLengthMinusOne = ChunkLength - 1; + private const int MaxChunkIndex = ChunkLength - 1; private readonly Stream stream; private readonly IManagedByteBuffer managedBuffer; + private MemoryHandle handle; + + private readonly byte* pinnedChunk; + private readonly byte[] bufferChunk; private readonly int length; - private int bytesRead; + private int chunkIndex; private int position; @@ -44,8 +49,11 @@ namespace SixLabors.ImageSharp.IO this.stream = stream; this.Position = (int)stream.Position; this.length = (int)stream.Length; - this.managedBuffer = memoryAllocator.AllocateManagedByteBuffer(ChunkLength, AllocationOptions.Clean); + this.managedBuffer = memoryAllocator.AllocateManagedByteBuffer(ChunkLength); this.bufferChunk = this.managedBuffer.Array; + this.handle = this.managedBuffer.Memory.Pin(); + this.pinnedChunk = (byte*)this.handle.Pointer; + this.chunkIndex = ChunkLength; } /// @@ -62,10 +70,20 @@ namespace SixLabors.ImageSharp.IO set { - // Reset everything. It's easier than tracking. - this.position = (int)value; - this.stream.Seek(this.position, SeekOrigin.Begin); - this.bytesRead = ChunkLength; + // Only reset chunkIndex if we are out of bounds of our working chunk + // otherwise we should simply move the value by the diff. + int v = (int)value; + if (this.IsInChunk(v, out int index)) + { + this.chunkIndex = index; + this.position = v; + } + else + { + this.position = v; + this.stream.Seek(value, SeekOrigin.Begin); + this.chunkIndex = ChunkLength; + } } } @@ -74,7 +92,7 @@ namespace SixLabors.ImageSharp.IO /// byte, or returns -1 if at the end of the stream. /// /// The unsigned byte cast to an , or -1 if at the end of the stream. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public int ReadByte() { if (this.position >= this.length) @@ -82,24 +100,21 @@ namespace SixLabors.ImageSharp.IO return -1; } - if (this.position == 0 || this.bytesRead > ChunkLengthMinusOne) + if (this.chunkIndex > MaxChunkIndex) { - return this.ReadByteSlow(); + this.FillChunk(); } this.position++; - return this.bufferChunk[this.bytesRead++]; + return this.pinnedChunk[this.chunkIndex++]; } /// /// Skips the number of bytes in the stream /// - /// The number of bytes to skip - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Skip(int count) - { - this.Position += count; - } + /// The number of bytes to skip. + [MethodImpl(InliningOptions.ShortMethod)] + public void Skip(int count) => this.Position += count; /// /// Reads a sequence of bytes from the current stream and advances the position within the stream @@ -120,36 +135,46 @@ namespace SixLabors.ImageSharp.IO /// of bytes requested if that many bytes are not currently available, or zero (0) /// if the end of the stream has been reached. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public int Read(byte[] buffer, int offset, int count) { - if (buffer.Length > ChunkLength) + if (count > ChunkLength) { return this.ReadToBufferSlow(buffer, offset, count); } - if (this.position == 0 || count + this.bytesRead > ChunkLength) + if (count + this.chunkIndex > ChunkLength) { return this.ReadToChunkSlow(buffer, offset, count); } - int n = this.GetCount(count); + int n = this.GetCopyCount(count); this.CopyBytes(buffer, offset, n); this.position += n; - this.bytesRead += n; - + this.chunkIndex += n; return n; } /// public void Dispose() { + this.handle.Dispose(); this.managedBuffer?.Dispose(); } - [MethodImpl(MethodImplOptions.NoInlining)] - private int ReadByteSlow() + [MethodImpl(InliningOptions.ShortMethod)] + private int GetPositionDifference(int p) => p - this.position; + + [MethodImpl(InliningOptions.ShortMethod)] + private bool IsInChunk(int p, out int index) + { + index = this.GetPositionDifference(p) + this.chunkIndex; + return index > -1 && index < ChunkLength; + } + + [MethodImpl(InliningOptions.ColdPath)] + private void FillChunk() { if (this.position != this.stream.Position) { @@ -157,34 +182,25 @@ namespace SixLabors.ImageSharp.IO } this.stream.Read(this.bufferChunk, 0, ChunkLength); - this.bytesRead = 0; - - this.position++; - return this.bufferChunk[this.bytesRead++]; + this.chunkIndex = 0; } - [MethodImpl(MethodImplOptions.NoInlining)] + [MethodImpl(InliningOptions.ColdPath)] private int ReadToChunkSlow(byte[] buffer, int offset, int count) { // Refill our buffer then copy. - if (this.position != this.stream.Position) - { - this.stream.Seek(this.position, SeekOrigin.Begin); - } + this.FillChunk(); - this.stream.Read(this.bufferChunk, 0, ChunkLength); - this.bytesRead = 0; - - int n = this.GetCount(count); + int n = this.GetCopyCount(count); this.CopyBytes(buffer, offset, n); this.position += n; - this.bytesRead += n; + this.chunkIndex += n; return n; } - [MethodImpl(MethodImplOptions.NoInlining)] + [MethodImpl(InliningOptions.ColdPath)] private int ReadToBufferSlow(byte[] buffer, int offset, int count) { // Read to target but don't copy to our chunk. @@ -195,11 +211,12 @@ namespace SixLabors.ImageSharp.IO int n = this.stream.Read(buffer, offset, count); this.Position += n; + return n; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private int GetCount(int count) + [MethodImpl(InliningOptions.ShortMethod)] + private int GetCopyCount(int count) { int n = this.length - this.position; if (n > count) @@ -215,23 +232,23 @@ namespace SixLabors.ImageSharp.IO return n; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] private void CopyBytes(byte[] buffer, int offset, int count) { if (count < 9) { int byteCount = count; - int read = this.bytesRead; - byte[] chunk = this.bufferChunk; + int read = this.chunkIndex; + byte* pinned = this.pinnedChunk; while (--byteCount > -1) { - buffer[offset + byteCount] = chunk[read + byteCount]; + buffer[offset + byteCount] = pinned[read + byteCount]; } } else { - Buffer.BlockCopy(this.bufferChunk, this.bytesRead, buffer, offset, count); + Buffer.BlockCopy(this.bufferChunk, this.chunkIndex, buffer, offset, count); } } } diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index f7f9e1d5f4..a124ddcacd 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -38,8 +38,8 @@ - - + + diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs index 5958b9f79d..5a4a9ab17c 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs @@ -47,5 +47,22 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg decoder.Dispose(); } } + + // RESULTS (2019 April 23): + // + // BenchmarkDotNet=v0.11.3, OS=Windows 10.0.17763.437 (1809/October2018Update/Redstone5) + // Intel Core i7-6600U CPU 2.60GHz (Skylake), 1 CPU, 4 logical and 2 physical cores + // .NET Core SDK=2.2.202 + // [Host] : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT + // Clr : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3362.0 + // Core : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT + // + // | Method | Job | Runtime | TestImage | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | + // |---------------------------- |----- |-------- |--------------------- |---------:|---------:|----------:|------:|--------:|---------:|------:|------:|----------:| + // | 'System.Drawing FULL' | Clr | Clr | Jpg/b(...)f.jpg [28] | 18.69 ms | 8.273 ms | 0.4535 ms | 1.00 | 0.00 | 343.7500 | - | - | 757.89 KB | + // | JpegDecoderCore.ParseStream | Clr | Clr | Jpg/b(...)f.jpg [28] | 15.76 ms | 4.266 ms | 0.2339 ms | 0.84 | 0.03 | - | - | - | 11.83 KB | + // | | | | | | | | | | | | | | + // | 'System.Drawing FULL' | Core | Core | Jpg/b(...)f.jpg [28] | 17.68 ms | 2.711 ms | 0.1486 ms | 1.00 | 0.00 | 343.7500 | - | - | 757.04 KB | + // | JpegDecoderCore.ParseStream | Core | Core | Jpg/b(...)f.jpg [28] | 14.27 ms | 3.671 ms | 0.2012 ms | 0.81 | 0.00 | - | - | - | 11.76 KB | } } \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs index 8247ba42b5..62742f729b 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs @@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg public ShortClr() { this.Add( - //Job.Clr.WithLaunchCount(1).WithWarmupCount(2).WithIterationCount(3), + // Job.Clr.WithLaunchCount(1).WithWarmupCount(2).WithIterationCount(3), Job.Core.WithLaunchCount(1).WithWarmupCount(2).WithIterationCount(3) ); } @@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg private byte[] jpegBytes; private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); - + [Params( TestImages.Jpeg.BenchmarkSuite.Lake_Small444YCbCr, TestImages.Jpeg.BenchmarkSuite.BadRstProgressive518_Large444YCbCr, @@ -87,7 +87,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg { using (var memoryStream = new MemoryStream(this.jpegBytes)) { - using (var image = Image.Load(memoryStream, new JpegDecoder(){ IgnoreMetadata = true})) + using (var image = Image.Load(memoryStream, new JpegDecoder() { IgnoreMetadata = true })) { return new CoreSize(image.Width, image.Height); } @@ -115,5 +115,24 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg // | | | | | | | | | | | // 'Decode Jpeg - System.Drawing' | Jpg/issues/issue750-exif-tranform.jpg | 95.192 ms | 3.1762 ms | 0.1795 ms | 1.00 | 0.00 | 1750.0000 | - | - | 5492.63 KB | // 'Decode Jpeg - ImageSharp' | Jpg/issues/issue750-exif-tranform.jpg | 230.158 ms | 48.8128 ms | 2.7580 ms | 2.42 | 0.02 | 312.5000 | 312.5000 | 312.5000 | 58834.66 KB | + + // RESULTS (2019 April 23): + // + //BenchmarkDotNet=v0.11.5, OS=Windows 10.0.17763.437 (1809/October2018Update/Redstone5) + //Intel Core i7-6600U CPU 2.60GHz (Skylake), 1 CPU, 4 logical and 2 physical cores + //.NET Core SDK=2.2.202 + // [Host] : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT + // Core : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT + // + //| Method | TestImage | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | + //|------------------------------- |--------------------- |-----------:|-----------:|-----------:|------:|--------:|----------:|------:|------:|------------:| + //| 'Decode Jpeg - System.Drawing' | Jpg/b(...)e.jpg [21] | 6.957 ms | 9.618 ms | 0.5272 ms | 1.00 | 0.00 | 93.7500 | - | - | 205.83 KB | + //| 'Decode Jpeg - ImageSharp' | Jpg/b(...)e.jpg [21] | 18.348 ms | 8.876 ms | 0.4865 ms | 2.65 | 0.23 | - | - | - | 14.49 KB | + //| | | | | | | | | | | | + //| 'Decode Jpeg - System.Drawing' | Jpg/b(...)f.jpg [28] | 18.687 ms | 11.632 ms | 0.6376 ms | 1.00 | 0.00 | 343.7500 | - | - | 757.04 KB | + //| 'Decode Jpeg - ImageSharp' | Jpg/b(...)f.jpg [28] | 41.990 ms | 25.514 ms | 1.3985 ms | 2.25 | 0.10 | - | - | - | 15.48 KB | + //| | | | | | | | | | | | + //| 'Decode Jpeg - System.Drawing' | Jpg/i(...)e.jpg [43] | 477.265 ms | 732.126 ms | 40.1303 ms | 1.00 | 0.00 | 3000.0000 | - | - | 7403.76 KB | + //| 'Decode Jpeg - ImageSharp' | Jpg/i(...)e.jpg [43] | 348.545 ms | 91.480 ms | 5.0143 ms | 0.73 | 0.06 | - | - | - | 35177.21 KB | } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DoubleBufferedStreams.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DoubleBufferedStreams.cs index d4cbe81e16..b0834eb52d 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DoubleBufferedStreams.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DoubleBufferedStreams.cs @@ -19,8 +19,8 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg private MemoryStream stream2; private MemoryStream stream3; private MemoryStream stream4; - DoubleBufferedStreamReader reader1; - DoubleBufferedStreamReader reader2; + private DoubleBufferedStreamReader reader1; + private DoubleBufferedStreamReader reader2; [GlobalSetup] public void CreateStreams() @@ -102,6 +102,19 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg return r; } + [Benchmark] + public int SimpleReadByte() + { + byte[] b = this.buffer; + int r = 0; + for (int i = 0; i < b.Length; i++) + { + r += b[i]; + } + + return r; + } + private static byte[] CreateTestBytes() { byte[] buffer = new byte[DoubleBufferedStreamReader.ChunkLength * 3]; @@ -111,4 +124,29 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg return buffer; } } + + // RESULTS (2019 April 24): + // + //BenchmarkDotNet=v0.11.5, OS=Windows 10.0.17763.437 (1809/October2018Update/Redstone5) + //Intel Core i7-6600U CPU 2.60GHz (Skylake), 1 CPU, 4 logical and 2 physical cores + //.NET Core SDK=2.2.202 + // [Host] : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT + // Clr : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3362.0 + // Core : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT + // + //IterationCount=3 LaunchCount=1 WarmupCount=3 + // + //| Method | Job | Runtime | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | + //|----------------------------- |----- |-------- |---------:|-----------:|----------:|------:|--------:|------:|------:|------:|----------:| + //| StandardStreamReadByte | Clr | Clr | 96.71 us | 5.9950 us | 0.3286 us | 1.00 | 0.00 | - | - | - | - | + //| StandardStreamRead | Clr | Clr | 77.73 us | 5.2284 us | 0.2866 us | 0.80 | 0.00 | - | - | - | - | + //| DoubleBufferedStreamReadByte | Clr | Clr | 23.17 us | 26.2354 us | 1.4381 us | 0.24 | 0.01 | - | - | - | - | + //| DoubleBufferedStreamRead | Clr | Clr | 33.35 us | 3.4071 us | 0.1868 us | 0.34 | 0.00 | - | - | - | - | + //| SimpleReadByte | Clr | Clr | 10.85 us | 0.4927 us | 0.0270 us | 0.11 | 0.00 | - | - | - | - | + //| | | | | | | | | | | | | + //| StandardStreamReadByte | Core | Core | 75.35 us | 12.9789 us | 0.7114 us | 1.00 | 0.00 | - | - | - | - | + //| StandardStreamRead | Core | Core | 55.36 us | 1.4432 us | 0.0791 us | 0.73 | 0.01 | - | - | - | - | + //| DoubleBufferedStreamReadByte | Core | Core | 21.47 us | 29.7076 us | 1.6284 us | 0.28 | 0.02 | - | - | - | - | + //| DoubleBufferedStreamRead | Core | Core | 29.67 us | 2.5988 us | 0.1424 us | 0.39 | 0.00 | - | - | - | - | + //| SimpleReadByte | Core | Core | 10.84 us | 0.7567 us | 0.0415 us | 0.14 | 0.00 | - | - | - | - | } diff --git a/tests/ImageSharp.Benchmarks/Codecs/MultiImageBenchmarkBase.cs b/tests/ImageSharp.Benchmarks/Codecs/MultiImageBenchmarkBase.cs index 75bbf21a33..84e2d06167 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/MultiImageBenchmarkBase.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/MultiImageBenchmarkBase.cs @@ -254,7 +254,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs protected void ForEachSystemDrawingImage(Func operation) { - using (MemoryStream workStream = new MemoryStream()) + using (var workStream = new MemoryStream()) { this.ForEachSystemDrawingImage( diff --git a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj index e600af7851..9af4d57cff 100644 --- a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj +++ b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj @@ -1,6 +1,6 @@  - netcoreapp2.1;net461 + netcoreapp2.1;net472 Exe True SixLabors.ImageSharp.Benchmarks diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index 73f97bab38..0b727f30ce 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -27,8 +27,8 @@ - - + + From 5283c6db6d7c01f2d01a84547afe1d44cee26c72 Mon Sep 17 00:00:00 2001 From: Brian Popow <38701097+brianpopow@users.noreply.github.com> Date: Sat, 27 Apr 2019 16:36:23 +0200 Subject: [PATCH 095/223] Feature: adaptive histogram equalization (#673) * first version of sliding window adaptive histogram equalization * going now from top to bottom of the image, added more comments * using memory allocator to create the histogram and the cdf * mirroring rows which exceeds the borders * mirroring also left and right borders * gridsize and cliplimit are now parameters of the constructor * using Parallel.For * only applying clipping once, effect applying it multiple times is neglectable * added abstract base class for histogram equalization, added option to enable / disable clipping * small improvements * clipLimit now in percent of the total number of pixels in the grid * optimization: only calculating the cdf until the maximum histogram index * fix: using configuration from the parameter instead of the default * removed unnecessary loops in CalculateCdf, fixed typo in method name AddPixelsToHistogram * added different approach for ahe: image is split up in tiles, cdf is computed for each tile. Grey value will be determined by interpolating between 4 tiles * simplified interpolation between the tiles * number of tiles is now fixed and depended on the width and height of the image * moved calculating LUT's into separate method * number of tiles is now part of the options and will be used with the sliding window approach also, so both methods are comparable * removed no longer valid xml comment * attempt fixing the borders * refactoring to improve readability * linear interpolation in the border tiles * refactored processing the borders into separate methods * fixing corner tiles * fixed build errors * fixing mistake during merge from upstream: setting test images to "update Resize reference output because of improved ResizeKernelMap calculations" * using Parallel.ForEach for all inner tile calculations * using Parallel.ForEach to calculate the lookup tables * re-using pre allocated pixel row in GetPixelRow * fixed issue with the border tiles, when tile width != tile height * changed default value for ClipHistogram to false again * alpha channel from the original image is now preserved * added unit tests for adaptive histogram equalization * Update External * 2x faster adaptive tiled processor * Remove double indexing and bounds checks * Begin optimizing the global histogram * Parallelize GlobalHistogramEqualizationProcessor * Moving sliding window from left to right instead of from top to bottom * The tile width and height is again depended on the image width: image.Width / Tiles * Removed keeping track of the maximum histogram position * Updated reference image for sliding window AHE for moving the sliding window from left to right * Removed unnecessary call to Span.Clear(), all values are overwritten anyway * Revert "Moving sliding window from left to right instead of from top to bottom" This reverts commit 8f19e5edd23f13fd1ddf93b4e795f82e7f1334bb. # Conflicts: # src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor.cs * Split GetPixelRow in two version: one which mirrors the edges (only needed in the borders of the images) and one which does not * Refactoring and cleanup sliding window processor * Added an upper limit of 100 tiles * Performance tweaks * Update External --- src/ImageSharp/Common/Helpers/ImageMaths.cs | 1 - .../HistogramEqualizationExtension.cs | 40 +- .../AdaptiveHistEqualizationProcessor.cs | 545 ++++++++++++++++++ .../AdaptiveHistEqualizationSWProcessor.cs | 389 +++++++++++++ .../GlobalHistogramEqualizationProcessor.cs | 106 ++++ .../HistogramEqualizationMethod.cs | 26 + .../HistogramEqualizationOptions.cs | 43 ++ .../HistogramEqualizationProcessor.cs | 138 +++-- .../General/BasicMath/Round.cs | 22 + .../HistogramEqualizationTests.cs | 87 ++- tests/ImageSharp.Tests/TestImages.cs | 1 + tests/Images/External | 2 +- .../Jpg/baseline/AsianCarvingLowContrast.jpg | 3 + 13 files changed, 1311 insertions(+), 92 deletions(-) create mode 100644 src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs create mode 100644 src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor.cs create mode 100644 src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs create mode 100644 src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationMethod.cs create mode 100644 src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationOptions.cs create mode 100644 tests/ImageSharp.Benchmarks/General/BasicMath/Round.cs create mode 100644 tests/Images/Input/Jpg/baseline/AsianCarvingLowContrast.jpg diff --git a/src/ImageSharp/Common/Helpers/ImageMaths.cs b/src/ImageSharp/Common/Helpers/ImageMaths.cs index 0c5b051809..64bcb11c9f 100644 --- a/src/ImageSharp/Common/Helpers/ImageMaths.cs +++ b/src/ImageSharp/Common/Helpers/ImageMaths.cs @@ -5,7 +5,6 @@ using System; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing.Processors.Transforms; using SixLabors.Primitives; namespace SixLabors.ImageSharp diff --git a/src/ImageSharp/Processing/HistogramEqualizationExtension.cs b/src/ImageSharp/Processing/HistogramEqualizationExtension.cs index 8dabfcc05c..d967ef3622 100644 --- a/src/ImageSharp/Processing/HistogramEqualizationExtension.cs +++ b/src/ImageSharp/Processing/HistogramEqualizationExtension.cs @@ -12,25 +12,51 @@ namespace SixLabors.ImageSharp.Processing public static class HistogramEqualizationExtension { /// - /// Equalizes the histogram of an image to increases the global contrast using 65536 luminance levels. + /// Equalizes the histogram of an image to increases the contrast. /// /// The pixel format. /// The image this method extends. /// The . public static IImageProcessingContext HistogramEqualization(this IImageProcessingContext source) where TPixel : struct, IPixel - => HistogramEqualization(source, 65536); + => HistogramEqualization(source, HistogramEqualizationOptions.Default); /// - /// Equalizes the histogram of an image to increases the global contrast. + /// Equalizes the histogram of an image to increases the contrast. /// /// The pixel format. /// The image this method extends. - /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images - /// or 65536 for 16-bit grayscale images. + /// The histogram equalization options to use. /// The . - public static IImageProcessingContext HistogramEqualization(this IImageProcessingContext source, int luminanceLevels) + public static IImageProcessingContext HistogramEqualization(this IImageProcessingContext source, HistogramEqualizationOptions options) where TPixel : struct, IPixel - => source.ApplyProcessor(new HistogramEqualizationProcessor(luminanceLevels)); + => source.ApplyProcessor(GetProcessor(options)); + + private static HistogramEqualizationProcessor GetProcessor(HistogramEqualizationOptions options) + where TPixel : struct, IPixel + { + HistogramEqualizationProcessor processor; + + switch (options.Method) + { + case HistogramEqualizationMethod.Global: + processor = new GlobalHistogramEqualizationProcessor(options.LuminanceLevels, options.ClipHistogram, options.ClipLimitPercentage); + break; + + case HistogramEqualizationMethod.AdaptiveTileInterpolation: + processor = new AdaptiveHistEqualizationProcessor(options.LuminanceLevels, options.ClipHistogram, options.ClipLimitPercentage, options.Tiles); + break; + + case HistogramEqualizationMethod.AdaptiveSlidingWindow: + processor = new AdaptiveHistEqualizationSWProcessor(options.LuminanceLevels, options.ClipHistogram, options.ClipLimitPercentage, options.Tiles); + break; + + default: + processor = new GlobalHistogramEqualizationProcessor(options.LuminanceLevels, options.ClipHistogram, options.ClipLimitPercentage); + break; + } + + return processor; + } } } diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs new file mode 100644 index 0000000000..cb52a88b7b --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs @@ -0,0 +1,545 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; +using System.Collections.Generic; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Memory; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Normalization +{ + /// + /// Applies an adaptive histogram equalization to the image. The image is split up in tiles. For each tile a cumulative distribution function (cdf) is calculated. + /// To calculate the final equalized pixel value, the cdf value of four adjacent tiles will be interpolated. + /// + /// The pixel format. + internal class AdaptiveHistEqualizationProcessor : HistogramEqualizationProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images + /// or 65536 for 16-bit grayscale images. + /// Indicating whether to clip the histogram bins at a specific value. + /// Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. + /// The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100. + public AdaptiveHistEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage, int tiles) + : base(luminanceLevels, clipHistogram, clipLimitPercentage) + { + Guard.MustBeGreaterThanOrEqualTo(tiles, 2, nameof(tiles)); + Guard.MustBeLessThanOrEqualTo(tiles, 100, nameof(tiles)); + + this.Tiles = tiles; + } + + /// + /// Gets the number of tiles the image is split into (horizontal and vertically) for the adaptive histogram equalization. + /// + private int Tiles { get; } + + /// + protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + int sourceWidth = source.Width; + int sourceHeight = source.Height; + int numberOfPixels = sourceWidth * sourceHeight; + int tileWidth = (int)MathF.Ceiling(sourceWidth / (float)this.Tiles); + int tileHeight = (int)MathF.Ceiling(sourceHeight / (float)this.Tiles); + int pixelsInTile = tileWidth * tileHeight; + int halfTileWidth = tileWidth / 2; + int halfTileHeight = tileHeight / 2; + int luminanceLevels = this.LuminanceLevels; + + // The image is split up into tiles. For each tile the cumulative distribution function will be calculated. + using (var cdfData = new CdfTileData(configuration, sourceWidth, sourceHeight, this.Tiles, this.Tiles, tileWidth, tileHeight, luminanceLevels)) + { + cdfData.CalculateLookupTables(source, this); + + var tileYStartPositions = new List<(int y, int cdfY)>(); + int cdfY = 0; + for (int y = halfTileHeight; y < sourceHeight - halfTileHeight; y += tileHeight) + { + tileYStartPositions.Add((y, cdfY)); + cdfY++; + } + + Parallel.For( + 0, + tileYStartPositions.Count, + new ParallelOptions() { MaxDegreeOfParallelism = configuration.MaxDegreeOfParallelism }, + index => + { + int cdfX = 0; + int tileX = 0; + int tileY = 0; + int y = tileYStartPositions[index].y; + int cdfYY = tileYStartPositions[index].cdfY; + + // It's unfortunate that we have to do this per iteration. + ref TPixel sourceBase = ref source.GetPixelReference(0, 0); + + cdfX = 0; + for (int x = halfTileWidth; x < sourceWidth - halfTileWidth; x += tileWidth) + { + tileY = 0; + int yEnd = Math.Min(y + tileHeight, sourceHeight); + int xEnd = Math.Min(x + tileWidth, sourceWidth); + for (int dy = y; dy < yEnd; dy++) + { + int dyOffSet = dy * sourceWidth; + tileX = 0; + for (int dx = x; dx < xEnd; dx++) + { + ref TPixel pixel = ref Unsafe.Add(ref sourceBase, dyOffSet + dx); + float luminanceEqualized = InterpolateBetweenFourTiles( + pixel, + cdfData, + this.Tiles, + this.Tiles, + tileX, + tileY, + cdfX, + cdfYY, + tileWidth, + tileHeight, + luminanceLevels); + + pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); + tileX++; + } + + tileY++; + } + + cdfX++; + } + }); + + ref TPixel pixelsBase = ref source.GetPixelReference(0, 0); + + // Fix left column + ProcessBorderColumn(ref pixelsBase, cdfData, 0, sourceWidth, sourceHeight, tileWidth, tileHeight, xStart: 0, xEnd: halfTileWidth, luminanceLevels); + + // Fix right column + int rightBorderStartX = ((this.Tiles - 1) * tileWidth) + halfTileWidth; + ProcessBorderColumn(ref pixelsBase, cdfData, this.Tiles - 1, sourceWidth, sourceHeight, tileWidth, tileHeight, xStart: rightBorderStartX, xEnd: sourceWidth, luminanceLevels); + + // Fix top row + ProcessBorderRow(ref pixelsBase, cdfData, 0, sourceWidth, tileWidth, yStart: 0, yEnd: halfTileHeight, luminanceLevels); + + // Fix bottom row + int bottomBorderStartY = ((this.Tiles - 1) * tileHeight) + halfTileHeight; + ProcessBorderRow(ref pixelsBase, cdfData, this.Tiles - 1, sourceWidth, tileWidth, yStart: bottomBorderStartY, yEnd: sourceHeight, luminanceLevels); + + // Left top corner + ProcessCornerTile(ref pixelsBase, cdfData, sourceWidth, 0, 0, xStart: 0, xEnd: halfTileWidth, yStart: 0, yEnd: halfTileHeight, luminanceLevels); + + // Left bottom corner + ProcessCornerTile(ref pixelsBase, cdfData, sourceWidth, 0, this.Tiles - 1, xStart: 0, xEnd: halfTileWidth, yStart: bottomBorderStartY, yEnd: sourceHeight, luminanceLevels); + + // Right top corner + ProcessCornerTile(ref pixelsBase, cdfData, sourceWidth, this.Tiles - 1, 0, xStart: rightBorderStartX, xEnd: sourceWidth, yStart: 0, yEnd: halfTileHeight, luminanceLevels); + + // Right bottom corner + ProcessCornerTile(ref pixelsBase, cdfData, sourceWidth, this.Tiles - 1, this.Tiles - 1, xStart: rightBorderStartX, xEnd: sourceWidth, yStart: bottomBorderStartY, yEnd: sourceHeight, luminanceLevels); + } + } + + /// + /// Processes the part of a corner tile which was previously left out. It consists of 1 / 4 of a tile and does not need interpolation. + /// + /// The output pixels base reference. + /// The lookup table to remap the grey values. + /// The source image width. + /// The x-position in the CDF lookup map. + /// The y-position in the CDF lookup map. + /// X start position. + /// X end position. + /// Y start position. + /// Y end position. + /// + /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images + /// or 65536 for 16-bit grayscale images. + /// + private static void ProcessCornerTile( + ref TPixel pixelsBase, + CdfTileData cdfData, + int sourceWidth, + int cdfX, + int cdfY, + int xStart, + int xEnd, + int yStart, + int yEnd, + int luminanceLevels) + { + for (int dy = yStart; dy < yEnd; dy++) + { + int dyOffSet = dy * sourceWidth; + for (int dx = xStart; dx < xEnd; dx++) + { + ref TPixel pixel = ref Unsafe.Add(ref pixelsBase, dyOffSet + dx); + float luminanceEqualized = cdfData.RemapGreyValue(cdfX, cdfY, GetLuminance(pixel, luminanceLevels)); + pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); + } + } + } + + /// + /// Processes a border column of the image which is half the size of the tile width. + /// + /// The output pixels reference. + /// The pre-computed lookup tables to remap the grey values for each tiles. + /// The X index of the lookup table to use. + /// The source image width. + /// The source image height. + /// The width of a tile. + /// The height of a tile. + /// X start position in the image. + /// X end position of the image. + /// + /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images + /// or 65536 for 16-bit grayscale images. + /// + private static void ProcessBorderColumn( + ref TPixel pixelBase, + CdfTileData cdfData, + int cdfX, + int sourceWidth, + int sourceHeight, + int tileWidth, + int tileHeight, + int xStart, + int xEnd, + int luminanceLevels) + { + int halfTileWidth = tileWidth / 2; + int halfTileHeight = tileHeight / 2; + + int cdfY = 0; + for (int y = halfTileHeight; y < sourceHeight - halfTileHeight; y += tileHeight) + { + int yLimit = Math.Min(y + tileHeight, sourceHeight - 1); + int tileY = 0; + for (int dy = y; dy < yLimit; dy++) + { + int dyOffSet = dy * sourceWidth; + int tileX = halfTileWidth; + for (int dx = xStart; dx < xEnd; dx++) + { + ref TPixel pixel = ref Unsafe.Add(ref pixelBase, dyOffSet + dx); + float luminanceEqualized = InterpolateBetweenTwoTiles(pixel, cdfData, cdfX, cdfY, cdfX, cdfY + 1, tileY, tileHeight, luminanceLevels); + pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); + tileX++; + } + + tileY++; + } + + cdfY++; + } + } + + /// + /// Processes a border row of the image which is half of the size of the tile height. + /// + /// The output pixels base reference. + /// The pre-computed lookup tables to remap the grey values for each tiles. + /// The Y index of the lookup table to use. + /// The source image width. + /// The width of a tile. + /// Y start position in the image. + /// Y end position of the image. + /// + /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images + /// or 65536 for 16-bit grayscale images. + /// + private static void ProcessBorderRow( + ref TPixel pixelBase, + CdfTileData cdfData, + int cdfY, + int sourceWidth, + int tileWidth, + int yStart, + int yEnd, + int luminanceLevels) + { + int halfTileWidth = tileWidth / 2; + + int cdfX = 0; + for (int x = halfTileWidth; x < sourceWidth - halfTileWidth; x += tileWidth) + { + int tileY = 0; + for (int dy = yStart; dy < yEnd; dy++) + { + int dyOffSet = dy * sourceWidth; + int tileX = 0; + int xLimit = Math.Min(x + tileWidth, sourceWidth - 1); + for (int dx = x; dx < xLimit; dx++) + { + ref TPixel pixel = ref Unsafe.Add(ref pixelBase, dyOffSet + dx); + float luminanceEqualized = InterpolateBetweenTwoTiles(pixel, cdfData, cdfX, cdfY, cdfX + 1, cdfY, tileX, tileWidth, luminanceLevels); + pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); + tileX++; + } + + tileY++; + } + + cdfX++; + } + } + + /// + /// Bilinear interpolation between four adjacent tiles. + /// + /// The pixel to remap the grey value from. + /// The pre-computed lookup tables to remap the grey values for each tiles. + /// The number of tiles in the x-direction. + /// The number of tiles in the y-direction. + /// X position inside the tile. + /// Y position inside the tile. + /// X index of the top left lookup table to use. + /// Y index of the top left lookup table to use. + /// Width of one tile in pixels. + /// Height of one tile in pixels. + /// + /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images + /// or 65536 for 16-bit grayscale images. + /// + /// A re-mapped grey value. + [MethodImpl(InliningOptions.ShortMethod)] + private static float InterpolateBetweenFourTiles( + TPixel sourcePixel, + CdfTileData cdfData, + int tileCountX, + int tileCountY, + int tileX, + int tileY, + int cdfX, + int cdfY, + int tileWidth, + int tileHeight, + int luminanceLevels) + { + int luminance = GetLuminance(sourcePixel, luminanceLevels); + float tx = tileX / (float)(tileWidth - 1); + float ty = tileY / (float)(tileHeight - 1); + + int yTop = cdfY; + int yBottom = Math.Min(tileCountY - 1, yTop + 1); + int xLeft = cdfX; + int xRight = Math.Min(tileCountX - 1, xLeft + 1); + + float cdfLeftTopLuminance = cdfData.RemapGreyValue(xLeft, yTop, luminance); + float cdfRightTopLuminance = cdfData.RemapGreyValue(xRight, yTop, luminance); + float cdfLeftBottomLuminance = cdfData.RemapGreyValue(xLeft, yBottom, luminance); + float cdfRightBottomLuminance = cdfData.RemapGreyValue(xRight, yBottom, luminance); + return BilinearInterpolation(tx, ty, cdfLeftTopLuminance, cdfRightTopLuminance, cdfLeftBottomLuminance, cdfRightBottomLuminance); + } + + /// + /// Linear interpolation between two tiles. + /// + /// The pixel to remap the grey value from. + /// The CDF lookup map. + /// X position inside the first tile. + /// Y position inside the first tile. + /// X position inside the second tile. + /// Y position inside the second tile. + /// Position inside the tile. + /// Width of the tile. + /// + /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images + /// or 65536 for 16-bit grayscale images. + /// + /// A re-mapped grey value. + [MethodImpl(InliningOptions.ShortMethod)] + private static float InterpolateBetweenTwoTiles( + TPixel sourcePixel, + CdfTileData cdfData, + int tileX1, + int tileY1, + int tileX2, + int tileY2, + int tilePos, + int tileWidth, + int luminanceLevels) + { + int luminance = GetLuminance(sourcePixel, luminanceLevels); + float tx = tilePos / (float)(tileWidth - 1); + + float cdfLuminance1 = cdfData.RemapGreyValue(tileX1, tileY1, luminance); + float cdfLuminance2 = cdfData.RemapGreyValue(tileX2, tileY2, luminance); + return LinearInterpolation(cdfLuminance1, cdfLuminance2, tx); + } + + /// + /// Bilinear interpolation between four tiles. + /// + /// The interpolation value in x direction in the range of [0, 1]. + /// The interpolation value in y direction in the range of [0, 1]. + /// Luminance from top left tile. + /// Luminance from right top tile. + /// Luminance from left bottom tile. + /// Luminance from right bottom tile. + /// Interpolated Luminance. + [MethodImpl(InliningOptions.ShortMethod)] + private static float BilinearInterpolation(float tx, float ty, float lt, float rt, float lb, float rb) + => LinearInterpolation(LinearInterpolation(lt, rt, tx), LinearInterpolation(lb, rb, tx), ty); + + /// + /// Linear interpolation between two grey values. + /// + /// The left value. + /// The right value. + /// The interpolation value between the two values in the range of [0, 1]. + /// The interpolated value. + [MethodImpl(InliningOptions.ShortMethod)] + private static float LinearInterpolation(float left, float right, float t) + => left + ((right - left) * t); + + /// + /// Contains the results of the cumulative distribution function for all tiles. + /// + private sealed class CdfTileData : IDisposable + { + private readonly Configuration configuration; + private readonly MemoryAllocator memoryAllocator; + + // Used for storing the minimum value for each CDF entry. + private readonly Buffer2D cdfMinBuffer2D; + + // Used for storing the LUT for each CDF entry. + private readonly Buffer2D cdfLutBuffer2D; + private readonly int pixelsInTile; + private readonly int sourceWidth; + private readonly int sourceHeight; + private readonly int tileWidth; + private readonly int tileHeight; + private readonly int luminanceLevels; + private readonly List<(int y, int cdfY)> tileYStartPositions; + + public CdfTileData( + Configuration configuration, + int sourceWidth, + int sourceHeight, + int tileCountX, + int tileCountY, + int tileWidth, + int tileHeight, + int luminanceLevels) + { + this.configuration = configuration; + this.memoryAllocator = configuration.MemoryAllocator; + this.luminanceLevels = luminanceLevels; + this.cdfMinBuffer2D = this.memoryAllocator.Allocate2D(tileCountX, tileCountY); + this.cdfLutBuffer2D = this.memoryAllocator.Allocate2D(tileCountX * luminanceLevels, tileCountY); + this.sourceWidth = sourceWidth; + this.sourceHeight = sourceHeight; + this.tileWidth = tileWidth; + this.tileHeight = tileHeight; + this.pixelsInTile = tileWidth * tileHeight; + + // Calculate the start positions and rent buffers. + this.tileYStartPositions = new List<(int y, int cdfY)>(); + int cdfY = 0; + for (int y = 0; y < sourceHeight; y += tileHeight) + { + this.tileYStartPositions.Add((y, cdfY)); + cdfY++; + } + } + + public void CalculateLookupTables(ImageFrame source, HistogramEqualizationProcessor processor) + { + int sourceWidth = this.sourceWidth; + int sourceHeight = this.sourceHeight; + int tileWidth = this.tileWidth; + int tileHeight = this.tileHeight; + int luminanceLevels = this.luminanceLevels; + MemoryAllocator memoryAllocator = this.memoryAllocator; + + Parallel.For( + 0, + this.tileYStartPositions.Count, + new ParallelOptions() { MaxDegreeOfParallelism = this.configuration.MaxDegreeOfParallelism }, + index => + { + int cdfX = 0; + int cdfY = this.tileYStartPositions[index].cdfY; + int y = this.tileYStartPositions[index].y; + int endY = Math.Min(y + tileHeight, sourceHeight); + ref TPixel sourceBase = ref source.GetPixelReference(0, 0); + ref int cdfMinBase = ref MemoryMarshal.GetReference(this.cdfMinBuffer2D.GetRowSpan(cdfY)); + + using (IMemoryOwner histogramBuffer = this.memoryAllocator.Allocate(luminanceLevels)) + { + Span histogram = histogramBuffer.GetSpan(); + ref int histogramBase = ref MemoryMarshal.GetReference(histogram); + + for (int x = 0; x < sourceWidth; x += tileWidth) + { + histogram.Clear(); + ref int cdfBase = ref MemoryMarshal.GetReference(this.GetCdfLutSpan(cdfX, index)); + + int xlimit = Math.Min(x + tileWidth, sourceWidth); + for (int dy = y; dy < endY; dy++) + { + int dyOffset = dy * sourceWidth; + for (int dx = x; dx < xlimit; dx++) + { + int luminace = GetLuminance(Unsafe.Add(ref sourceBase, dyOffset + dx), luminanceLevels); + histogram[luminace]++; + } + } + + if (processor.ClipHistogramEnabled) + { + processor.ClipHistogram(histogram, processor.ClipLimitPercentage, this.pixelsInTile); + } + + Unsafe.Add(ref cdfMinBase, cdfX) = processor.CalculateCdf(ref cdfBase, ref histogramBase, histogram.Length - 1); + + cdfX++; + } + } + }); + } + + [MethodImpl(InliningOptions.ShortMethod)] + public Span GetCdfLutSpan(int tileX, int tileY) => this.cdfLutBuffer2D.GetRowSpan(tileY).Slice(tileX * this.luminanceLevels, this.luminanceLevels); + + /// + /// Remaps the grey value with the cdf. + /// + /// The tiles x-position. + /// The tiles y-position. + /// The original luminance. + /// The remapped luminance. + [MethodImpl(InliningOptions.ShortMethod)] + public float RemapGreyValue(int tilesX, int tilesY, int luminance) + { + int cdfMin = this.cdfMinBuffer2D[tilesX, tilesY]; + Span cdfSpan = this.GetCdfLutSpan(tilesX, tilesY); + return (this.pixelsInTile - cdfMin) == 0 + ? cdfSpan[luminance] / this.pixelsInTile + : cdfSpan[luminance] / (float)(this.pixelsInTile - cdfMin); + } + + public void Dispose() + { + this.cdfMinBuffer2D.Dispose(); + this.cdfLutBuffer2D.Dispose(); + } + } + } +} diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor.cs new file mode 100644 index 0000000000..aa9b530c6d --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor.cs @@ -0,0 +1,389 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Memory; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Normalization +{ + /// + /// Applies an adaptive histogram equalization to the image using an sliding window approach. + /// + /// The pixel format. + internal class AdaptiveHistEqualizationSWProcessor : HistogramEqualizationProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images + /// or 65536 for 16-bit grayscale images. + /// Indicating whether to clip the histogram bins at a specific value. + /// Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. + /// The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100. + public AdaptiveHistEqualizationSWProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage, int tiles) + : base(luminanceLevels, clipHistogram, clipLimitPercentage) + { + Guard.MustBeGreaterThanOrEqualTo(tiles, 2, nameof(tiles)); + Guard.MustBeLessThanOrEqualTo(tiles, 100, nameof(tiles)); + + this.Tiles = tiles; + } + + /// + /// Gets the number of tiles the image is split into (horizontal and vertically) for the adaptive histogram equalization. + /// + private int Tiles { get; } + + /// + protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + MemoryAllocator memoryAllocator = configuration.MemoryAllocator; + int numberOfPixels = source.Width * source.Height; + Span pixels = source.GetPixelSpan(); + + var parallelOptions = new ParallelOptions() { MaxDegreeOfParallelism = configuration.MaxDegreeOfParallelism }; + int tileWidth = source.Width / this.Tiles; + int tileHeight = tileWidth; + int pixeInTile = tileWidth * tileHeight; + int halfTileHeight = tileHeight / 2; + int halfTileWidth = halfTileHeight; + var slidingWindowInfos = new SlidingWindowInfos(tileWidth, tileHeight, halfTileWidth, halfTileHeight, pixeInTile); + + using (Buffer2D targetPixels = configuration.MemoryAllocator.Allocate2D(source.Width, source.Height)) + { + // Process the inner tiles, which do not require to check the borders. + Parallel.For( + halfTileWidth, + source.Width - halfTileWidth, + parallelOptions, + this.ProcessSlidingWindow( + source, + memoryAllocator, + targetPixels, + slidingWindowInfos, + yStart: halfTileHeight, + yEnd: source.Height - halfTileHeight, + useFastPath: true, + configuration)); + + // Process the left border of the image. + Parallel.For( + 0, + halfTileWidth, + parallelOptions, + this.ProcessSlidingWindow( + source, + memoryAllocator, + targetPixels, + slidingWindowInfos, + yStart: 0, + yEnd: source.Height, + useFastPath: false, + configuration)); + + // Process the right border of the image. + Parallel.For( + source.Width - halfTileWidth, + source.Width, + parallelOptions, + this.ProcessSlidingWindow( + source, + memoryAllocator, + targetPixels, + slidingWindowInfos, + yStart: 0, + yEnd: source.Height, + useFastPath: false, + configuration)); + + // Process the top border of the image. + Parallel.For( + halfTileWidth, + source.Width - halfTileWidth, + parallelOptions, + this.ProcessSlidingWindow( + source, + memoryAllocator, + targetPixels, + slidingWindowInfos, + yStart: 0, + yEnd: halfTileHeight, + useFastPath: false, + configuration)); + + // Process the bottom border of the image. + Parallel.For( + halfTileWidth, + source.Width - halfTileWidth, + parallelOptions, + this.ProcessSlidingWindow( + source, + memoryAllocator, + targetPixels, + slidingWindowInfos, + yStart: source.Height - halfTileHeight, + yEnd: source.Height, + useFastPath: false, + configuration)); + + Buffer2D.SwapOrCopyContent(source.PixelBuffer, targetPixels); + } + } + + /// + /// Applies the sliding window equalization to one column of the image. The window is moved from top to bottom. + /// Moving the window one pixel down requires to remove one row from the top of the window from the histogram and + /// adding a new row at the bottom. + /// + /// The source image. + /// The memory allocator. + /// The target pixels. + /// Informations about the sliding window dimensions. + /// The y start position. + /// The y end position. + /// if set to true the borders of the image will not be checked. + /// The configuration. + /// Action Delegate. + private Action ProcessSlidingWindow( + ImageFrame source, + MemoryAllocator memoryAllocator, + Buffer2D targetPixels, + SlidingWindowInfos swInfos, + int yStart, + int yEnd, + bool useFastPath, + Configuration configuration) + { + return x => + { + using (IMemoryOwner histogramBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean)) + using (IMemoryOwner histogramBufferCopy = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean)) + using (IMemoryOwner cdfBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean)) + using (IMemoryOwner pixelRowBuffer = memoryAllocator.Allocate(swInfos.TileWidth, AllocationOptions.Clean)) + { + Span histogram = histogramBuffer.GetSpan(); + ref int histogramBase = ref MemoryMarshal.GetReference(histogram); + + Span histogramCopy = histogramBufferCopy.GetSpan(); + ref int histogramCopyBase = ref MemoryMarshal.GetReference(histogramCopy); + + ref int cdfBase = ref MemoryMarshal.GetReference(cdfBuffer.GetSpan()); + + Span pixelRow = pixelRowBuffer.GetSpan(); + ref Vector4 pixelRowBase = ref MemoryMarshal.GetReference(pixelRow); + + // Build the initial histogram of grayscale values. + for (int dy = yStart - swInfos.HalfTileHeight; dy < yStart + swInfos.HalfTileHeight; dy++) + { + if (useFastPath) + { + this.CopyPixelRowFast(source, pixelRow, x - swInfos.HalfTileWidth, dy, swInfos.TileWidth, configuration); + } + else + { + this.CopyPixelRow(source, pixelRow, x - swInfos.HalfTileWidth, dy, swInfos.TileWidth, configuration); + } + + this.AddPixelsToHistogram(ref pixelRowBase, ref histogramBase, this.LuminanceLevels, pixelRow.Length); + } + + for (int y = yStart; y < yEnd; y++) + { + if (this.ClipHistogramEnabled) + { + // Clipping the histogram, but doing it on a copy to keep the original un-clipped values for the next iteration. + histogram.CopyTo(histogramCopy); + this.ClipHistogram(histogramCopy, this.ClipLimitPercentage, swInfos.PixeInTile); + } + + // Calculate the cumulative distribution function, which will map each input pixel in the current tile to a new value. + int cdfMin = this.ClipHistogramEnabled + ? this.CalculateCdf(ref cdfBase, ref histogramCopyBase, histogram.Length - 1) + : this.CalculateCdf(ref cdfBase, ref histogramBase, histogram.Length - 1); + + float numberOfPixelsMinusCdfMin = swInfos.PixeInTile - cdfMin; + + // Map the current pixel to the new equalized value. + int luminance = GetLuminance(source[x, y], this.LuminanceLevels); + float luminanceEqualized = Unsafe.Add(ref cdfBase, luminance) / numberOfPixelsMinusCdfMin; + targetPixels[x, y].FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, source[x, y].ToVector4().W)); + + // Remove top most row from the histogram, mirroring rows which exceeds the borders. + if (useFastPath) + { + this.CopyPixelRowFast(source, pixelRow, x - swInfos.HalfTileWidth, y - swInfos.HalfTileWidth, swInfos.TileWidth, configuration); + } + else + { + this.CopyPixelRow(source, pixelRow, x - swInfos.HalfTileWidth, y - swInfos.HalfTileWidth, swInfos.TileWidth, configuration); + } + + this.RemovePixelsFromHistogram(ref pixelRowBase, ref histogramBase, this.LuminanceLevels, pixelRow.Length); + + // Add new bottom row to the histogram, mirroring rows which exceeds the borders. + if (useFastPath) + { + this.CopyPixelRowFast(source, pixelRow, x - swInfos.HalfTileWidth, y + swInfos.HalfTileWidth, swInfos.TileWidth, configuration); + } + else + { + this.CopyPixelRow(source, pixelRow, x - swInfos.HalfTileWidth, y + swInfos.HalfTileWidth, swInfos.TileWidth, configuration); + } + + this.AddPixelsToHistogram(ref pixelRowBase, ref histogramBase, this.LuminanceLevels, pixelRow.Length); + } + } + }; + } + + /// + /// Get the a pixel row at a given position with a length of the tile width. Mirrors pixels which exceeds the edges. + /// + /// The source image. + /// Pre-allocated pixel row span of the size of a the tile width. + /// The x position. + /// The y position. + /// The width in pixels of a tile. + /// The configuration. + private void CopyPixelRow( + ImageFrame source, + Span rowPixels, + int x, + int y, + int tileWidth, + Configuration configuration) + { + if (y < 0) + { + y = ImageMaths.FastAbs(y); + } + else if (y >= source.Height) + { + int diff = y - source.Height; + y = source.Height - diff - 1; + } + + // Special cases for the left and the right border where GetPixelRowSpan can not be used. + if (x < 0) + { + rowPixels.Clear(); + int idx = 0; + for (int dx = x; dx < x + tileWidth; dx++) + { + rowPixels[idx] = source[ImageMaths.FastAbs(dx), y].ToVector4(); + idx++; + } + + return; + } + else if (x + tileWidth > source.Width) + { + rowPixels.Clear(); + int idx = 0; + for (int dx = x; dx < x + tileWidth; dx++) + { + if (dx >= source.Width) + { + int diff = dx - source.Width; + rowPixels[idx] = source[dx - diff - 1, y].ToVector4(); + } + else + { + rowPixels[idx] = source[dx, y].ToVector4(); + } + + idx++; + } + + return; + } + + this.CopyPixelRowFast(source, rowPixels, x, y, tileWidth, configuration); + } + + /// + /// Get the a pixel row at a given position with a length of the tile width. + /// + /// The source image. + /// Pre-allocated pixel row span of the size of a the tile width. + /// The x position. + /// The y position. + /// The width in pixels of a tile. + /// The configuration. + [MethodImpl(InliningOptions.ShortMethod)] + private void CopyPixelRowFast( + ImageFrame source, + Span rowPixels, + int x, + int y, + int tileWidth, + Configuration configuration) + => PixelOperations.Instance.ToVector4(configuration, source.GetPixelRowSpan(y).Slice(start: x, length: tileWidth), rowPixels); + + /// + /// Adds a column of grey values to the histogram. + /// + /// The reference to the span of grey values to add. + /// The reference to the histogram span. + /// The number of different luminance levels. + /// The grey values span length. + [MethodImpl(InliningOptions.ShortMethod)] + private void AddPixelsToHistogram(ref Vector4 greyValuesBase, ref int histogramBase, int luminanceLevels, int length) + { + for (int idx = 0; idx < length; idx++) + { + int luminance = GetLuminance(ref Unsafe.Add(ref greyValuesBase, idx), luminanceLevels); + Unsafe.Add(ref histogramBase, luminance)++; + } + } + + /// + /// Removes a column of grey values from the histogram. + /// + /// The reference to the span of grey values to remove. + /// The reference to the histogram span. + /// The number of different luminance levels. + /// The grey values span length. + [MethodImpl(InliningOptions.ShortMethod)] + private void RemovePixelsFromHistogram(ref Vector4 greyValuesBase, ref int histogramBase, int luminanceLevels, int length) + { + for (int idx = 0; idx < length; idx++) + { + int luminance = GetLuminance(ref Unsafe.Add(ref greyValuesBase, idx), luminanceLevels); + Unsafe.Add(ref histogramBase, luminance)--; + } + } + + private class SlidingWindowInfos + { + public SlidingWindowInfos(int tileWidth, int tileHeight, int halfTileWidth, int halfTileHeight, int pixeInTile) + { + this.TileWidth = tileWidth; + this.TileHeight = tileHeight; + this.HalfTileWidth = halfTileWidth; + this.HalfTileHeight = halfTileHeight; + this.PixeInTile = pixeInTile; + } + + public int TileWidth { get; private set; } + + public int TileHeight { get; private set; } + + public int PixeInTile { get; private set; } + + public int HalfTileWidth { get; private set; } + + public int HalfTileHeight { get; private set; } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs new file mode 100644 index 0000000000..aadde2424b --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs @@ -0,0 +1,106 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Memory; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Normalization +{ + /// + /// Applies a global histogram equalization to the image. + /// + /// The pixel format. + internal class GlobalHistogramEqualizationProcessor : HistogramEqualizationProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + /// + /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images + /// or 65536 for 16-bit grayscale images. + /// + /// Indicating whether to clip the histogram bins at a specific value. + /// Histogram clip limit in percent of the total pixels. Histogram bins which exceed this limit, will be capped at this value. + public GlobalHistogramEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage) + : base(luminanceLevels, clipHistogram, clipLimitPercentage) + { + } + + /// + protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + MemoryAllocator memoryAllocator = configuration.MemoryAllocator; + int numberOfPixels = source.Width * source.Height; + Span pixels = source.GetPixelSpan(); + var workingRect = new Rectangle(0, 0, source.Width, source.Height); + + using (IMemoryOwner histogramBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean)) + using (IMemoryOwner cdfBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean)) + { + // Build the histogram of the grayscale levels. + ParallelHelper.IterateRows( + workingRect, + configuration, + rows => + { + ref int histogramBase = ref MemoryMarshal.GetReference(histogramBuffer.GetSpan()); + for (int y = rows.Min; y < rows.Max; y++) + { + ref TPixel pixelBase = ref MemoryMarshal.GetReference(source.GetPixelRowSpan(y)); + + for (int x = 0; x < workingRect.Width; x++) + { + int luminance = GetLuminance(Unsafe.Add(ref pixelBase, x), this.LuminanceLevels); + Unsafe.Add(ref histogramBase, luminance)++; + } + } + }); + + Span histogram = histogramBuffer.GetSpan(); + if (this.ClipHistogramEnabled) + { + this.ClipHistogram(histogram, this.ClipLimitPercentage, numberOfPixels); + } + + // Calculate the cumulative distribution function, which will map each input pixel to a new value. + int cdfMin = this.CalculateCdf( + ref MemoryMarshal.GetReference(cdfBuffer.GetSpan()), + ref MemoryMarshal.GetReference(histogram), + histogram.Length - 1); + + float numberOfPixelsMinusCdfMin = numberOfPixels - cdfMin; + + // Apply the cdf to each pixel of the image + ParallelHelper.IterateRows( + workingRect, + configuration, + rows => + { + ref int cdfBase = ref MemoryMarshal.GetReference(cdfBuffer.GetSpan()); + for (int y = rows.Min; y < rows.Max; y++) + { + ref TPixel pixelBase = ref MemoryMarshal.GetReference(source.GetPixelRowSpan(y)); + + for (int x = 0; x < workingRect.Width; x++) + { + ref TPixel pixel = ref Unsafe.Add(ref pixelBase, x); + int luminance = GetLuminance(pixel, this.LuminanceLevels); + float luminanceEqualized = Unsafe.Add(ref cdfBase, luminance) / numberOfPixelsMinusCdfMin; + pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); + } + } + }); + } + } + } +} diff --git a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationMethod.cs b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationMethod.cs new file mode 100644 index 0000000000..641587c394 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationMethod.cs @@ -0,0 +1,26 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Processing.Processors.Normalization +{ + /// + /// Enumerates the different types of defined histogram equalization methods. + /// + public enum HistogramEqualizationMethod : int + { + /// + /// A global histogram equalization. + /// + Global, + + /// + /// Adaptive histogram equalization using a tile interpolation approach. + /// + AdaptiveTileInterpolation, + + /// + /// Adaptive histogram equalization using sliding window. Slower then the tile interpolation mode, but can yield to better results. + /// + AdaptiveSlidingWindow, + } +} diff --git a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationOptions.cs b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationOptions.cs new file mode 100644 index 0000000000..0d1a378361 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationOptions.cs @@ -0,0 +1,43 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Processing.Processors.Normalization +{ + /// + /// Data container providing the different options for the histogram equalization. + /// + public class HistogramEqualizationOptions + { + /// + /// Gets the default instance. + /// + public static HistogramEqualizationOptions Default { get; } = new HistogramEqualizationOptions(); + + /// + /// Gets or sets the histogram equalization method to use. Defaults to global histogram equalization. + /// + public HistogramEqualizationMethod Method { get; set; } = HistogramEqualizationMethod.Global; + + /// + /// Gets or sets the number of different luminance levels. Typical values are 256 for 8-bit grayscale images + /// or 65536 for 16-bit grayscale images. Defaults to 256. + /// + public int LuminanceLevels { get; set; } = 256; + + /// + /// Gets or sets a value indicating whether to clip the histogram bins at a specific value. Defaults to false. + /// + public bool ClipHistogram { get; set; } = false; + + /// + /// Gets or sets the histogram clip limit in percent of the total pixels in a tile. Histogram bins which exceed this limit, will be capped at this value. + /// Defaults to 0.035f. + /// + public float ClipLimitPercentage { get; set; } = 0.035f; + + /// + /// Gets or sets the number of tiles the image is split into (horizontal and vertically) for the adaptive histogram equalization. Defaults to 10. + /// + public int Tiles { get; set; } = 10; + } +} diff --git a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs index 580adc7fe9..fd1b6b9784 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs @@ -2,34 +2,38 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Buffers; using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Memory; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Normalization { /// - /// Applies a global histogram equalization to the image. + /// Defines a processor that normalizes the histogram of an image. /// /// The pixel format. - internal class HistogramEqualizationProcessor : ImageProcessor + internal abstract class HistogramEqualizationProcessor : ImageProcessor where TPixel : struct, IPixel { + private readonly float luminanceLevelsFloat; + /// /// Initializes a new instance of the class. /// /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images /// or 65536 for 16-bit grayscale images. - public HistogramEqualizationProcessor(int luminanceLevels) + /// Indicates, if histogram bins should be clipped. + /// Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. + protected HistogramEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage) { Guard.MustBeGreaterThan(luminanceLevels, 0, nameof(luminanceLevels)); + Guard.MustBeGreaterThan(clipLimitPercentage, 0F, nameof(clipLimitPercentage)); this.LuminanceLevels = luminanceLevels; + this.luminanceLevelsFloat = luminanceLevels; + this.ClipHistogramEnabled = clipHistogram; + this.ClipLimitPercentage = clipLimitPercentage; } /// @@ -37,77 +41,77 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// public int LuminanceLevels { get; } - /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + /// + /// Gets a value indicating whether to clip the histogram bins at a specific value. + /// + public bool ClipHistogramEnabled { get; } + + /// + /// Gets the histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. + /// + public float ClipLimitPercentage { get; } + + /// + /// Calculates the cumulative distribution function. + /// + /// The reference to the array holding the cdf. + /// The reference to the histogram of the input image. + /// Index of the maximum of the histogram. + /// The first none zero value of the cdf. + public int CalculateCdf(ref int cdfBase, ref int histogramBase, int maxIdx) { - MemoryAllocator memoryAllocator = configuration.MemoryAllocator; - int numberOfPixels = source.Width * source.Height; - Span pixels = source.GetPixelSpan(); + int histSum = 0; + int cdfMin = 0; + bool cdfMinFound = false; - // Build the histogram of the grayscale levels. - using (IMemoryOwner histogramBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean)) - using (IMemoryOwner cdfBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean)) + for (int i = 0; i <= maxIdx; i++) { - Span histogram = histogramBuffer.GetSpan(); - for (int i = 0; i < pixels.Length; i++) + histSum += Unsafe.Add(ref histogramBase, i); + if (!cdfMinFound && histSum != 0) { - TPixel sourcePixel = pixels[i]; - int luminance = this.GetLuminance(sourcePixel, this.LuminanceLevels); - histogram[luminance]++; + cdfMin = histSum; + cdfMinFound = true; } - // Calculate the cumulative distribution function, which will map each input pixel to a new value. - Span cdf = cdfBuffer.GetSpan(); - int cdfMin = this.CalculateCdf(cdf, histogram); - - // Apply the cdf to each pixel of the image - float numberOfPixelsMinusCdfMin = numberOfPixels - cdfMin; - for (int i = 0; i < pixels.Length; i++) - { - TPixel sourcePixel = pixels[i]; - - int luminance = this.GetLuminance(sourcePixel, this.LuminanceLevels); - float luminanceEqualized = cdf[luminance] / numberOfPixelsMinusCdfMin; - - pixels[i].FromVector4(new Vector4(luminanceEqualized)); - } + // Creating the lookup table: subtracting cdf min, so we do not need to do that inside the for loop. + Unsafe.Add(ref cdfBase, i) = Math.Max(0, histSum - cdfMin); } + + return cdfMin; } /// - /// Calculates the cumulative distribution function. + /// AHE tends to over amplify the contrast in near-constant regions of the image, since the histogram in such regions is highly concentrated. + /// Clipping the histogram is meant to reduce this effect, by cutting of histogram bin's which exceed a certain amount and redistribute + /// the values over the clip limit to all other bins equally. /// - /// The array holding the cdf. - /// The histogram of the input image. - /// The first none zero value of the cdf. - private int CalculateCdf(Span cdf, Span histogram) + /// The histogram to apply the clipping. + /// Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. + /// The numbers of pixels inside the tile. + public void ClipHistogram(Span histogram, float clipLimitPercentage, int pixelCount) { - // Calculate the cumulative histogram - int histSum = 0; - for (int i = 0; i < histogram.Length; i++) - { - histSum += histogram[i]; - cdf[i] = histSum; - } + int clipLimit = (int)MathF.Round(pixelCount * clipLimitPercentage); + int sumOverClip = 0; + ref int histogramBase = ref MemoryMarshal.GetReference(histogram); - // Get the first none zero value of the cumulative histogram - int cdfMin = 0; for (int i = 0; i < histogram.Length; i++) { - if (cdf[i] != 0) + ref int histogramLevel = ref Unsafe.Add(ref histogramBase, i); + if (histogramLevel > clipLimit) { - cdfMin = cdf[i]; - break; + sumOverClip += histogramLevel - clipLimit; + histogramLevel = clipLimit; } } - // Creating the lookup table: subtracting cdf min, so we do not need to do that inside the for loop - for (int i = 0; i < histogram.Length; i++) + int addToEachBin = sumOverClip > 0 ? (int)MathF.Floor(sumOverClip / this.luminanceLevelsFloat) : 0; + if (addToEachBin > 0) { - cdf[i] = Math.Max(0, cdf[i] - cdfMin); + for (int i = 0; i < histogram.Length; i++) + { + Unsafe.Add(ref histogramBase, i) += addToEachBin; + } } - - return cdfMin; } /// @@ -116,13 +120,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// The pixel to get the luminance from /// The number of luminance levels (256 for 8 bit, 65536 for 16 bit grayscale images) [MethodImpl(InliningOptions.ShortMethod)] - private int GetLuminance(TPixel sourcePixel, int luminanceLevels) + public static int GetLuminance(TPixel sourcePixel, int luminanceLevels) { - // Convert to grayscale using ITU-R Recommendation BT.709 var vector = sourcePixel.ToVector4(); - int luminance = Convert.ToInt32(((.2126F * vector.X) + (.7152F * vector.Y) + (.0722F * vector.Y)) * (luminanceLevels - 1)); - - return luminance; + return GetLuminance(ref vector, luminanceLevels); } + + /// + /// Convert the pixel values to grayscale using ITU-R Recommendation BT.709. + /// + /// The vector to get the luminance from + /// The number of luminance levels (256 for 8 bit, 65536 for 16 bit grayscale images) + [MethodImpl(InliningOptions.ShortMethod)] + public static int GetLuminance(ref Vector4 vector, int luminanceLevels) + => (int)MathF.Round(((.2126F * vector.X) + (.7152F * vector.Y) + (.0722F * vector.Y)) * (luminanceLevels - 1)); } -} +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/BasicMath/Round.cs b/tests/ImageSharp.Benchmarks/General/BasicMath/Round.cs new file mode 100644 index 0000000000..2c18b2972c --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/BasicMath/Round.cs @@ -0,0 +1,22 @@ +using System; +using BenchmarkDotNet.Attributes; + +namespace SixLabors.ImageSharp.Benchmarks.General.BasicMath +{ + public class Round + { + private const float input = .51F; + + [Benchmark] + public int ConvertTo() => Convert.ToInt32(input); + + [Benchmark] + public int MathRound() => (int)Math.Round(input); + + // Results 20th Jan 2019 + // Method | Mean | Error | StdDev | Median | + //---------- |----------:|----------:|----------:|----------:| + // ConvertTo | 3.1967 ns | 0.1234 ns | 0.2129 ns | 3.2340 ns | + // MathRound | 0.0528 ns | 0.0374 ns | 0.1079 ns | 0.0000 ns | + } +} diff --git a/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs b/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs index 1595ed32cc..84d592bd96 100644 --- a/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs +++ b/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs @@ -3,12 +3,16 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Normalization; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Normalization { public class HistogramEqualizationTests { + private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.0456F); + [Theory] [InlineData(256)] [InlineData(65536)] @@ -27,18 +31,19 @@ namespace SixLabors.ImageSharp.Tests.Processing.Normalization 70, 87, 69, 68, 65, 73, 78, 90 }; - var image = new Image(8, 8); - for (int y = 0; y < 8; y++) + using (var image = new Image(8, 8)) { - for (int x = 0; x < 8; x++) + for (int y = 0; y < 8; y++) { - byte luminance = pixels[y * 8 + x]; - image[x, y] = new Rgba32(luminance, luminance, luminance); + for (int x = 0; x < 8; x++) + { + byte luminance = pixels[(y * 8) + x]; + image[x, y] = new Rgba32(luminance, luminance, luminance); + } } - } - byte[] expected = new byte[] - { + byte[] expected = new byte[] + { 0, 12, 53, 32, 146, 53, 174, 53, 57, 32, 12, 227, 219, 202, 32, 154, 65, 85, 93, 239, 251, 227, 65, 158, @@ -47,22 +52,66 @@ namespace SixLabors.ImageSharp.Tests.Processing.Normalization 117, 190, 36, 190, 178, 93, 20, 170, 130, 202, 73, 20, 12, 53, 85, 194, 146, 206, 130, 117, 85, 166, 182, 215 - }; + }; - // Act - image.Mutate(x => x.HistogramEqualization(luminanceLevels)); + // Act + image.Mutate(x => x.HistogramEqualization(new HistogramEqualizationOptions() + { + LuminanceLevels = luminanceLevels + })); - // Assert - for (int y = 0; y < 8; y++) - { - for (int x = 0; x < 8; x++) + // Assert + for (int y = 0; y < 8; y++) { - Rgba32 actual = image[x, y]; - Assert.Equal(expected[y * 8 + x], actual.R); - Assert.Equal(expected[y * 8 + x], actual.G); - Assert.Equal(expected[y * 8 + x], actual.B); + for (int x = 0; x < 8; x++) + { + Rgba32 actual = image[x, y]; + Assert.Equal(expected[(y * 8) + x], actual.R); + Assert.Equal(expected[(y * 8) + x], actual.G); + Assert.Equal(expected[(y * 8) + x], actual.B); + } } } } + + [Theory] + [WithFile(TestImages.Jpeg.Baseline.LowContrast, PixelTypes.Rgba32)] + public void Adaptive_SlidingWindow_15Tiles_WithClipping(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + var options = new HistogramEqualizationOptions() + { + Method = HistogramEqualizationMethod.AdaptiveSlidingWindow, + LuminanceLevels = 256, + ClipHistogram = true, + Tiles = 15 + }; + image.Mutate(x => x.HistogramEqualization(options)); + image.DebugSave(provider); + image.CompareToReferenceOutput(ValidatorComparer, provider); + } + } + + [Theory] + [WithFile(TestImages.Jpeg.Baseline.LowContrast, PixelTypes.Rgba32)] + public void Adaptive_TileInterpolation_10Tiles_WithClipping(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + var options = new HistogramEqualizationOptions() + { + Method = HistogramEqualizationMethod.AdaptiveTileInterpolation, + LuminanceLevels = 256, + ClipHistogram = true, + Tiles = 10 + }; + image.Mutate(x => x.HistogramEqualization(options)); + image.DebugSave(provider); + image.CompareToReferenceOutput(ValidatorComparer, provider); + } + } } } diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 8b2fd2e7f8..23bebd6213 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -141,6 +141,7 @@ namespace SixLabors.ImageSharp.Tests public const string Testorig420 = "Jpg/baseline/testorig.jpg"; public const string MultiScanBaselineCMYK = "Jpg/baseline/MultiScanBaselineCMYK.jpg"; public const string Ratio1x1 = "Jpg/baseline/ratio-1x1.jpg"; + public const string LowContrast = "Jpg/baseline/AsianCarvingLowContrast.jpg"; public const string Testorig12bit = "Jpg/baseline/testorig12.jpg"; public const string YcckSubsample1222 = "Jpg/baseline/ycck-subsample-1222.jpg"; diff --git a/tests/Images/External b/tests/Images/External index 8693e2fd45..1ca5154996 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 8693e2fd4577a9ac1a749da8db564095b5a05389 +Subproject commit 1ca515499663e8b0b7c924a49b8d212f7447bdb0 diff --git a/tests/Images/Input/Jpg/baseline/AsianCarvingLowContrast.jpg b/tests/Images/Input/Jpg/baseline/AsianCarvingLowContrast.jpg new file mode 100644 index 0000000000..d16a2864d1 --- /dev/null +++ b/tests/Images/Input/Jpg/baseline/AsianCarvingLowContrast.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:50f6359d228079ec5e6ead84046119eda84136026c1651c753e6d270405cd4b7 +size 187216 From 570849085be1f3a449eb3aad73d5aab75778b783 Mon Sep 17 00:00:00 2001 From: BorisTheBrave Date: Sat, 27 Apr 2019 23:27:04 +0100 Subject: [PATCH 096/223] ImageBrush shouldn't Dispose of the image it is using. (#883) Fixes #881 --- .../Processing/ImageBrush{TPixel}.cs | 1 - .../Drawing/FillImageBrushTests.cs | 36 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 tests/ImageSharp.Tests/Drawing/FillImageBrushTests.cs diff --git a/src/ImageSharp.Drawing/Processing/ImageBrush{TPixel}.cs b/src/ImageSharp.Drawing/Processing/ImageBrush{TPixel}.cs index 1ef4bb9ec9..cfb3b2ea4f 100644 --- a/src/ImageSharp.Drawing/Processing/ImageBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/ImageBrush{TPixel}.cs @@ -113,7 +113,6 @@ namespace SixLabors.ImageSharp.Processing /// public override void Dispose() { - this.source.Dispose(); } /// diff --git a/tests/ImageSharp.Tests/Drawing/FillImageBrushTests.cs b/tests/ImageSharp.Tests/Drawing/FillImageBrushTests.cs new file mode 100644 index 0000000000..772f62d5cc --- /dev/null +++ b/tests/ImageSharp.Tests/Drawing/FillImageBrushTests.cs @@ -0,0 +1,36 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Primitives; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; +using SixLabors.Primitives; +using SixLabors.Shapes; + +using Xunit; + +// ReSharper disable InconsistentNaming + +namespace SixLabors.ImageSharp.Tests.Drawing +{ + [GroupOutput("Drawing")] + public class FillImageBrushTests + { + [Fact] + public void DoesNotDisposeImage() + { + using (var src = new Image(5, 5)) + { + var brush = new ImageBrush(src); + using (var dest = new Image(10, 10)) + { + dest.Mutate(c => c.Fill(brush, new Rectangle(0, 0, 10, 10))); + dest.Mutate(c => c.Fill(brush, new Rectangle(0, 0, 10, 10))); + } + } + } + } +} \ No newline at end of file From 764a73b6310d4fb21af72a90d3704f499fccebb5 Mon Sep 17 00:00:00 2001 From: Anthony Truskinger Date: Tue, 30 Apr 2019 21:55:44 +1000 Subject: [PATCH 097/223] Now throws a better excpetion DrawImage source does not overlap target (#877) * No longer throws when DrawImage source does not overlap target Previously, when DrawImage was used to overlay an image, in cases where the source image did not overlap the target image, a very confusing error was reported: "System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values. Parameter name: MaxDegreeOfParallelism" Now, when this case happens, the DrawImage method will simply not affect the target image, which is the same way FillRegionProcessor handles such cases. ParallelHelper.IterRows also now does more validation of the input rectangle so that any further cases of this kind of problem throw a more relvant exception. Note I switched from DebugGuard to Guard because IterRows is a public API and thus should always validate its inputs. Fixes #875 * Refines DrawImage non-overlap error logic Adresses PR feedback in #877. Changes DrawImage shortcut to be less than or equal to. Also changes maxX calculation to use `Right` over `Width`. This is a semantic change that reflects intention better. No actual change because Top,Left for that rectanngle should always be 0,0. And adds more informative argument names to ParallelHelper.IterRows error message. * Non-overlapping DrawImage now throws Adressing PR feedback from #877 DrawImage now throws when the source image does not overlap, with a useful error message. Also improved the error messages for IterRows (and added validation to the other IterRows method) * DrawImage overlap test changed to support RELEASE The tests on the CI server are run in RELEASE which wrap the expected exception with an ImageProcessingException. * Adress feedback for DrawImage exception DrawImage throws an ImageProcessor exception which makes it easier to catch. And reverted IterRows to use Guard helpers --- .../Processors/Drawing/DrawImageProcessor.cs | 8 +++- .../Common/ParallelUtils/ParallelHelper.cs | 17 +++++++- .../ImageSharp.Tests/Drawing/DrawImageTest.cs | 42 +++++++++++++++++++ .../Helpers/ParallelHelperTests.cs | 35 ++++++++++++++++ 4 files changed, 100 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs index 0957904c62..6c03537eb3 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs @@ -71,7 +71,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing Rectangle bounds = targetImage.Bounds(); int minX = Math.Max(this.Location.X, sourceRectangle.X); - int maxX = Math.Min(this.Location.X + bounds.Width, sourceRectangle.Width); + int maxX = Math.Min(this.Location.X + bounds.Width, sourceRectangle.Right); int targetX = minX - this.Location.X; int minY = Math.Max(this.Location.Y, sourceRectangle.Y); @@ -81,6 +81,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY); + // not a valid operation because rectangle does not overlap with this image. + if (workingRect.Width <= 0 || workingRect.Height <= 0) + { + throw new ImageProcessingException("Cannot draw image because the source image does not overlap the target image."); + } + ParallelHelper.IterateRows( workingRect, configuration, diff --git a/src/ImageSharp/Common/ParallelUtils/ParallelHelper.cs b/src/ImageSharp/Common/ParallelUtils/ParallelHelper.cs index a930b8390f..2c2d045a57 100644 --- a/src/ImageSharp/Common/ParallelUtils/ParallelHelper.cs +++ b/src/ImageSharp/Common/ParallelUtils/ParallelHelper.cs @@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.ParallelUtils in ParallelExecutionSettings parallelSettings, Action body) { - DebugGuard.MustBeGreaterThan(rectangle.Width, 0, nameof(rectangle)); + ValidateRectangle(rectangle); int maxSteps = DivideCeil(rectangle.Width * rectangle.Height, parallelSettings.MinimumPixelsProcessedPerTask); @@ -87,6 +87,8 @@ namespace SixLabors.ImageSharp.ParallelUtils Action> body) where T : unmanaged { + ValidateRectangle(rectangle); + int maxSteps = DivideCeil(rectangle.Width * rectangle.Height, parallelSettings.MinimumPixelsProcessedPerTask); int numOfSteps = Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps); @@ -142,5 +144,18 @@ namespace SixLabors.ImageSharp.ParallelUtils [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int DivideCeil(int dividend, int divisor) => 1 + ((dividend - 1) / divisor); + + private static void ValidateRectangle(Rectangle rectangle) + { + Guard.MustBeGreaterThan( + rectangle.Width, + 0, + $"{nameof(rectangle)}.{nameof(rectangle.Width)}"); + + Guard.MustBeGreaterThan( + rectangle.Height, + 0, + $"{nameof(rectangle)}.{nameof(rectangle.Height)}"); + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs b/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs index 374454afba..b07f508834 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs @@ -131,6 +131,48 @@ namespace SixLabors.ImageSharp.Tests background.DebugSave(provider, testOutputDetails: "Positive"); } } + [Theory] + [WithSolidFilledImages(100, 100, 255, 255, 255, PixelTypes.Rgba32)] + public void ImageShouldHandlePositiveLocationTruncatedOverlay(TestImageProvider provider) + { + using (Image background = provider.GetImage()) + using (var overlay = new Image(50, 50)) + { + overlay.Mutate(x => x.Fill(Rgba32.Black)); + + const int xy = 75; + Rgba32 backgroundPixel = background[xy - 1, xy - 1]; + Rgba32 overlayPixel = overlay[0, 0]; + + background.Mutate(x => x.DrawImage(overlay, new Point(xy, xy), PixelColorBlendingMode.Normal, 1F)); + + Assert.Equal(Rgba32.White, backgroundPixel); + Assert.Equal(overlayPixel, background[xy, xy]); + + background.DebugSave(provider, testOutputDetails: "PositiveTruncated"); + } + } + + [Theory] + [WithSolidFilledImages(100, 100, 255, 255, 255, PixelTypes.Rgba32, -30, -30)] + [WithSolidFilledImages(100, 100, 255, 255, 255, PixelTypes.Rgba32, 130, -30)] + [WithSolidFilledImages(100, 100, 255, 255, 255, PixelTypes.Rgba32, 130, 130)] + [WithSolidFilledImages(100, 100, 255, 255, 255, PixelTypes.Rgba32, -30, 130)] + public void NonOverlappingImageThrows(TestImageProvider provider, int x, int y) + { + using (Image background = provider.GetImage()) + using (var overlay = new Image(Configuration.Default, 10, 10, Rgba32.Black)) + { + ImageProcessingException ex = Assert.Throws(Test); + + Assert.Contains("does not overlap", ex.ToString()); + + void Test() + { + background.Mutate(context => context.DrawImage(overlay, new Point(x, y), GraphicsOptions.Default)); + } + } + } private static void VerifyImage( TestImageProvider provider, diff --git a/tests/ImageSharp.Tests/Helpers/ParallelHelperTests.cs b/tests/ImageSharp.Tests/Helpers/ParallelHelperTests.cs index ef6b133f75..ee309e0e30 100644 --- a/tests/ImageSharp.Tests/Helpers/ParallelHelperTests.cs +++ b/tests/ImageSharp.Tests/Helpers/ParallelHelperTests.cs @@ -9,6 +9,7 @@ using System.Threading; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.PixelFormats; using SixLabors.Memory; using SixLabors.Primitives; @@ -334,5 +335,39 @@ namespace SixLabors.ImageSharp.Tests.Helpers TestImageExtensions.CompareBuffers(expected.Span, actual.Span); } } + + [Theory] + [InlineData(0, 10)] + [InlineData(10, 0)] + [InlineData(-10, 10)] + [InlineData(10, -10)] + public void IterateRowsRequiresValidRectangle(int width, int height) + { + var parallelSettings = new ParallelExecutionSettings(); + + var rect = new Rectangle(0, 0, width, height); + + ArgumentOutOfRangeException ex = Assert.Throws( + () => ParallelHelper.IterateRows(rect, parallelSettings, (rows) => { })); + + Assert.Contains(width <= 0 ? "Width" : "Height", ex.Message); + } + + [Theory] + [InlineData(0, 10)] + [InlineData(10, 0)] + [InlineData(-10, 10)] + [InlineData(10, -10)] + public void IterateRowsWithTempBufferRequiresValidRectangle(int width, int height) + { + var parallelSettings = new ParallelExecutionSettings(); + + var rect = new Rectangle(0, 0, width, height); + + ArgumentOutOfRangeException ex = Assert.Throws( + () => ParallelHelper.IterateRowsWithTempBuffer(rect, parallelSettings, (rows, memory) => { })); + + Assert.Contains(width <= 0 ? "Width" : "Height", ex.Message); + } } } \ No newline at end of file From 7fda30d01d225bdc6d287fe82a1c64f0393b5efd Mon Sep 17 00:00:00 2001 From: Brian Popow <38701097+brianpopow@users.noreply.github.com> Date: Wed, 1 May 2019 16:03:50 +0200 Subject: [PATCH 098/223] Bitmap encoder writes V3 header as default (#889) * Bitmap encoder will now write a V3 header as default. Introduces a new encoder option `SupportTransparency`: With this option a V4 header will be written with BITFIELDS compression. * Add 4 and 16 bit images to the Identify test * Add some useful links for bitmap documentation * Add 32 bpp images to the Identify test * Added further information on what will change for the encoder, if SupportTransparency is used. --- src/ImageSharp/Formats/Bmp/BmpEncoder.cs | 8 ++++ src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs | 45 ++++++++++++------- .../Formats/Bmp/IBmpEncoderOptions.cs | 10 ++++- src/ImageSharp/Formats/Bmp/README.md | 19 +++++--- .../Formats/Bmp/BmpDecoderTests.cs | 7 ++- .../Formats/Bmp/BmpEncoderTests.cs | 38 ++++++++++++++-- 6 files changed, 100 insertions(+), 27 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoder.cs b/src/ImageSharp/Formats/Bmp/BmpEncoder.cs index b1a66accec..4efdedb349 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoder.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoder.cs @@ -18,6 +18,14 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// public BmpBitsPerPixel? BitsPerPixel { get; set; } + /// + /// Gets or sets a value indicating whether the encoder should support transparency. + /// Note: Transparency support only works together with 32 bits per pixel. This option will + /// change the default behavior of the encoder of writing a bitmap version 3 info header with no compression. + /// Instead a bitmap version 4 info header will be written with the BITFIELDS compression. + /// + public bool SupportTransparency { get; set; } + /// public void Encode(Image image, Stream stream) where TPixel : struct, IPixel diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index ad526f6689..c4c51b78e3 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -24,22 +24,22 @@ namespace SixLabors.ImageSharp.Formats.Bmp private int padding; /// - /// The mask for the alpha channel of the color for a 32 bit rgba bitmaps. + /// The mask for the alpha channel of the color for 32 bit rgba bitmaps. /// private const int Rgba32AlphaMask = 0xFF << 24; /// - /// The mask for the red part of the color for a 32 bit rgba bitmaps. + /// The mask for the red part of the color for 32 bit rgba bitmaps. /// private const int Rgba32RedMask = 0xFF << 16; /// - /// The mask for the green part of the color for a 32 bit rgba bitmaps. + /// The mask for the green part of the color for 32 bit rgba bitmaps. /// private const int Rgba32GreenMask = 0xFF << 8; /// - /// The mask for the blue part of the color for a 32 bit rgba bitmaps. + /// The mask for the blue part of the color for 32 bit rgba bitmaps. /// private const int Rgba32BlueMask = 0xFF; @@ -49,15 +49,23 @@ namespace SixLabors.ImageSharp.Formats.Bmp private BmpBitsPerPixel? bitsPerPixel; + /// + /// A bitmap v4 header will only be written, if the user explicitly wants support for transparency. + /// In this case the compression type BITFIELDS will be used. + /// Otherwise a bitmap v3 header will be written, which is supported by almost all decoders. + /// + private readonly bool writeV4Header; + /// /// Initializes a new instance of the class. /// - /// The encoder options - /// The memory manager + /// The encoder options. + /// The memory manager. public BmpEncoderCore(IBmpEncoderOptions options, MemoryAllocator memoryAllocator) { this.memoryAllocator = memoryAllocator; this.bitsPerPixel = options.BitsPerPixel; + this.writeV4Header = options.SupportTransparency; } /// @@ -112,7 +120,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp } } - int infoHeaderSize = BmpInfoHeader.SizeV4; + int infoHeaderSize = this.writeV4Header ? BmpInfoHeader.SizeV4 : BmpInfoHeader.SizeV3; var infoHeader = new BmpInfoHeader( headerSize: infoHeaderSize, height: image.Height, @@ -123,17 +131,15 @@ namespace SixLabors.ImageSharp.Formats.Bmp clrUsed: 0, clrImportant: 0, xPelsPerMeter: hResolution, - yPelsPerMeter: vResolution) - { - RedMask = Rgba32RedMask, - GreenMask = Rgba32GreenMask, - BlueMask = Rgba32BlueMask, - Compression = BmpCompression.BitFields - }; + yPelsPerMeter: vResolution); - if (this.bitsPerPixel == BmpBitsPerPixel.Pixel32) + if (this.writeV4Header && this.bitsPerPixel == BmpBitsPerPixel.Pixel32) { infoHeader.AlphaMask = Rgba32AlphaMask; + infoHeader.RedMask = Rgba32RedMask; + infoHeader.GreenMask = Rgba32GreenMask; + infoHeader.BlueMask = Rgba32BlueMask; + infoHeader.Compression = BmpCompression.BitFields; } var fileHeader = new BmpFileHeader( @@ -151,7 +157,14 @@ namespace SixLabors.ImageSharp.Formats.Bmp stream.Write(buffer, 0, BmpFileHeader.Size); - infoHeader.WriteV4Header(buffer); + if (this.writeV4Header) + { + infoHeader.WriteV4Header(buffer); + } + else + { + infoHeader.WriteV3Header(buffer); + } stream.Write(buffer, 0, infoHeaderSize); diff --git a/src/ImageSharp/Formats/Bmp/IBmpEncoderOptions.cs b/src/ImageSharp/Formats/Bmp/IBmpEncoderOptions.cs index f62504d08f..a3a056bfa2 100644 --- a/src/ImageSharp/Formats/Bmp/IBmpEncoderOptions.cs +++ b/src/ImageSharp/Formats/Bmp/IBmpEncoderOptions.cs @@ -6,12 +6,20 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// /// Configuration options for use during bmp encoding /// - /// The encoder can currently only write 24-bit rgb images to streams. + /// The encoder can currently only write 24-bit and 32-bit rgb images to streams. internal interface IBmpEncoderOptions { /// /// Gets the number of bits per pixel. /// BmpBitsPerPixel? BitsPerPixel { get; } + + /// + /// Gets a value indicating whether the encoder should support transparency. + /// Note: Transparency support only works together with 32 bits per pixel. This option will + /// change the default behavior of the encoder of writing a bitmap version 3 info header with no compression. + /// Instead a bitmap version 4 info header will be written with the BITFIELDS compression. + /// + bool SupportTransparency { get; } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Bmp/README.md b/src/ImageSharp/Formats/Bmp/README.md index d072838438..f418307335 100644 --- a/src/ImageSharp/Formats/Bmp/README.md +++ b/src/ImageSharp/Formats/Bmp/README.md @@ -1,8 +1,17 @@ -Encoder/Decoder adapted from: +### Encoder/Decoder adapted from: -https://github.com/yufeih/Nine.Imaging/ -https://imagetools.codeplex.com/ +- [Nine.Imaging](https://github.com/yufeih/Nine.Imaging/) +- [imagetools.codeplex](https://imagetools.codeplex.com/) -TODO: +### Some useful links for documentation about the bitmap format: -- Add support for all bitmap formats. +- [Microsoft Windows Bitmap File](http://www.fileformat.info/format/bmp/egff.htm) +- [OS/2 Bitmap File Format Summary](http://www.fileformat.info/format/os2bmp/egff.htm) +- [The DIB File Format](https://www-user.tu-chemnitz.de/~heha/viewchm.php/hs/petzold.chm/petzoldi/ch15b.htm) +- [Dr.Dobbs: The BMP File Format, Part 1](http://www.drdobbs.com/architecture-and-design/the-bmp-file-format-part-1/184409517) +- [Windows Bitmap File Format Specifications](ftp://ftp.nada.kth.se/pub/hacks/sgi/src/libwmf/doc/Bmpfrmat.html) + +### A set of bitmap test images: + +- [bmpsuite](http://entropymine.com/jason/bmpsuite/bmpsuite/html/bmpsuite.html) +- [eclecticgeek](http://eclecticgeek.com/dompdf/core_tests/image_bmp.html) \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index 05fadd4584..5f3cc7db47 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -130,7 +130,7 @@ namespace SixLabors.ImageSharp.Tests [Theory] [WithFile(LessThanFullSizedPalette, PixelTypes.Rgba32)] - public void BmpDecoder_CanDecodeLessThanFullPalete(TestImageProvider provider) + public void BmpDecoder_CanDecodeLessThanFullPalette(TestImageProvider provider) where TPixel : struct, IPixel { using (Image image = provider.GetImage(new BmpDecoder())) @@ -213,11 +213,16 @@ namespace SixLabors.ImageSharp.Tests } [Theory] + [InlineData(Bit32Rgb, 32)] + [InlineData(Bit32Rgba, 32)] [InlineData(Car, 24)] [InlineData(F, 24)] [InlineData(NegHeight, 24)] + [InlineData(Bit16, 16)] + [InlineData(Bit16Inverted, 16)] [InlineData(Bit8, 8)] [InlineData(Bit8Inverted, 8)] + [InlineData(Bit4, 4)] public void Identify(string imagePath, int expectedPixelSize) { var testFile = TestFile.Create(imagePath); diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs index c9745ef4f7..c7558ab422 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs @@ -11,6 +11,8 @@ using Xunit.Abstractions; namespace SixLabors.ImageSharp.Tests { + using static TestImages.Bmp; + public class BmpEncoderTests : FileTestBase { public static readonly TheoryData BitsPerPixel = @@ -102,15 +104,43 @@ namespace SixLabors.ImageSharp.Tests public void Encode_WorksWithDifferentSizes(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) where TPixel : struct, IPixel => TestBmpEncoderCore(provider, bitsPerPixel); - private static void TestBmpEncoderCore(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) + [Theory] + [WithFile(Bit32Rgb, PixelTypes.Rgba32 | PixelTypes.Rgb24, BmpBitsPerPixel.Pixel32)] + [WithFile(Bit32Rgba, PixelTypes.Rgba32 | PixelTypes.Rgb24, BmpBitsPerPixel.Pixel32)] + [WithFile(WinBmpv4, PixelTypes.Rgba32 | PixelTypes.Rgb24, BmpBitsPerPixel.Pixel32)] + [WithFile(WinBmpv5, PixelTypes.Rgba32 | PixelTypes.Rgb24, BmpBitsPerPixel.Pixel32)] + // WinBmpv3 is a 24 bits per pixel image + [WithFile(WinBmpv3, PixelTypes.Rgb24, BmpBitsPerPixel.Pixel24)] + public void Encode_WithV3Header_Works(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) + // if supportTransparency is false, a v3 bitmap header will be written + where TPixel : struct, IPixel => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: false); + + [Theory] + [WithFile(Bit32Rgb, PixelTypes.Rgba32 | PixelTypes.Rgb24, BmpBitsPerPixel.Pixel32)] + [WithFile(Bit32Rgba, PixelTypes.Rgba32 | PixelTypes.Rgb24, BmpBitsPerPixel.Pixel32)] + [WithFile(WinBmpv4, PixelTypes.Rgba32 | PixelTypes.Rgb24, BmpBitsPerPixel.Pixel32)] + [WithFile(WinBmpv5, PixelTypes.Rgba32 | PixelTypes.Rgb24, BmpBitsPerPixel.Pixel32)] + [WithFile(WinBmpv3, PixelTypes.Rgb24, BmpBitsPerPixel.Pixel24)] + public void Encode_WithV4Header_Works(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) + where TPixel : struct, IPixel => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: true); + + [Theory] + [WithFile(TestImages.Png.GrayAlpha2BitInterlaced, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel32)] + public void Encode_PreservesAlpha(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) + where TPixel : struct, IPixel => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: true); + + private static void TestBmpEncoderCore(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel, bool supportTransparency = true) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) { - // there is no alpha in bmp! - image.Mutate(c => c.MakeOpaque()); + // There is no alpha in bmp with 24 bits per pixels, so the reference image will be made opaque. + if (bitsPerPixel == BmpBitsPerPixel.Pixel24) + { + image.Mutate(c => c.MakeOpaque()); + } - var encoder = new BmpEncoder { BitsPerPixel = bitsPerPixel }; + var encoder = new BmpEncoder { BitsPerPixel = bitsPerPixel, SupportTransparency = supportTransparency }; // Does DebugSave & load reference CompareToReferenceInput(): image.VerifyEncoder(provider, "bmp", bitsPerPixel, encoder); From 039eae6a99c7d19a3a8ce927aacfa9841567a3e2 Mon Sep 17 00:00:00 2001 From: Brian Popow <38701097+brianpopow@users.noreply.github.com> Date: Fri, 3 May 2019 17:10:40 +0200 Subject: [PATCH 099/223] Add support for encoding 16 bit per pixel bitmaps (#899) * Implemented encoding of 16 bits per pixel bitmaps * Add unit tests for 16 bit encoding and Bgra5551 conversion * Add additional Bgra5551 pixel conversion tests * Add Bgra5551 tests for Short2/4 and HalfVector2/4 * Use scaled vector conversion --- src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs | 5 + src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 3 +- src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs | 30 ++++ .../Formats/Bmp/IBmpEncoderOptions.cs | 2 +- src/ImageSharp/PixelFormats/IPixel.cs | 6 + .../PixelImplementations/Alpha8.cs | 4 + .../PixelImplementations/Argb32.cs | 4 + .../PixelImplementations/Bgr24.cs | 4 + .../PixelImplementations/Bgr565.cs | 4 + .../PixelImplementations/Bgra32.cs | 4 + .../PixelImplementations/Bgra4444.cs | 4 + .../PixelImplementations/Bgra5551.cs | 4 + .../PixelImplementations/Byte4.cs | 4 + .../PixelImplementations/Gray16.cs | 4 + .../PixelImplementations/Gray8.cs | 4 + .../PixelImplementations/HalfSingle.cs | 4 + .../PixelImplementations/HalfVector2.cs | 4 + .../PixelImplementations/HalfVector4.cs | 4 + .../PixelImplementations/NormalizedByte2.cs | 11 +- .../PixelImplementations/NormalizedByte4.cs | 4 + .../PixelImplementations/NormalizedShort2.cs | 4 + .../PixelImplementations/NormalizedShort4.cs | 4 + .../PixelFormats/PixelImplementations/Rg32.cs | 4 + .../PixelImplementations/Rgb24.cs | 4 + .../PixelImplementations/Rgb48.cs | 4 + .../PixelImplementations/Rgba1010102.cs | 4 + .../PixelImplementations/Rgba32.cs | 4 + .../PixelImplementations/Rgba64.cs | 6 +- .../PixelImplementations/RgbaVector.cs | 9 +- .../PixelImplementations/Short2.cs | 9 +- .../PixelImplementations/Short4.cs | 4 + .../PixelOperations{TPixel}.Generated.cs | 72 ++++++++++ .../PixelOperations{TPixel}.Generated.tt | 3 + .../Formats/Bmp/BmpDecoderTests.cs | 2 +- .../Formats/Bmp/BmpEncoderTests.cs | 17 ++- .../PixelFormats/Alpha8Tests.cs | 14 ++ .../PixelFormats/Argb32Tests.cs | 14 ++ .../PixelFormats/Bgr24Tests.cs | 15 ++ .../PixelFormats/Bgr565Tests.cs | 14 ++ .../PixelFormats/Bgra32Tests.cs | 14 ++ .../PixelFormats/Bgra4444Tests.cs | 14 ++ .../PixelFormats/Bgra5551Tests.cs | 129 +++++++++++++++++- .../PixelFormats/Byte4Tests.cs | 14 ++ .../PixelFormats/Gray16Tests.cs | 14 ++ .../PixelFormats/Gray8Tests.cs | 14 ++ .../PixelFormats/HalfVector2Tests.cs | 17 +++ .../PixelFormats/HalfVector4Tests.cs | 14 ++ .../PixelFormats/NormalizedByte2Tests.cs | 14 ++ .../PixelFormats/NormalizedByte4Tests.cs | 14 ++ .../PixelFormats/NormalizedShort2Tests.cs | 14 ++ .../PixelFormats/NormalizedShort4Tests.cs | 14 ++ .../PixelFormats/Rg32Tests.cs | 14 ++ .../PixelFormats/Rgb24Tests.cs | 22 ++- .../PixelFormats/Rgb48Tests.cs | 16 +++ .../PixelFormats/Rgba1010102Tests.cs | 14 ++ .../PixelFormats/Rgba32Tests.cs | 14 ++ .../PixelFormats/Rgba64Tests.cs | 17 +++ .../PixelFormats/Short2Tests.cs | 17 +++ .../PixelFormats/Short4Tests.cs | 14 ++ tests/ImageSharp.Tests/TestImages.cs | 1 + .../TestUtilities/PixelTypes.cs | 2 + tests/Images/Input/Bmp/rgb16.bmp | 3 + 62 files changed, 715 insertions(+), 25 deletions(-) create mode 100644 tests/Images/Input/Bmp/rgb16.bmp diff --git a/src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs b/src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs index 618999c87d..38f5c1d662 100644 --- a/src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs +++ b/src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs @@ -8,6 +8,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// public enum BmpBitsPerPixel : short { + /// + /// 16 bits per pixel. Each pixel consists of 2 bytes. + /// + Pixel16 = 16, + /// /// 24 bits per pixel. Each pixel consists of 3 bytes. /// diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index b52c3754e2..9d9c7b624a 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -1022,7 +1022,8 @@ namespace SixLabors.ImageSharp.Formats.Bmp this.bmpMetadata.InfoHeaderType = infoHeaderType; // We can only encode at these bit rates so far. - if (bitsPerPixel.Equals((short)BmpBitsPerPixel.Pixel24) + if (bitsPerPixel.Equals((short)BmpBitsPerPixel.Pixel16) + || bitsPerPixel.Equals((short)BmpBitsPerPixel.Pixel24) || bitsPerPixel.Equals((short)BmpBitsPerPixel.Pixel32)) { this.bmpMetadata.BitsPerPixel = (BmpBitsPerPixel)bitsPerPixel; diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index c4c51b78e3..82483e3902 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -194,6 +194,10 @@ namespace SixLabors.ImageSharp.Formats.Bmp case BmpBitsPerPixel.Pixel24: this.Write24Bit(stream, pixels); break; + + case BmpBitsPerPixel.Pixel16: + this.Write16Bit(stream, pixels); + break; } } @@ -246,5 +250,31 @@ namespace SixLabors.ImageSharp.Formats.Bmp } } } + + /// + /// Writes the 16bit color palette to the stream. + /// + /// The type of the pixel. + /// The to write to. + /// The containing pixel data. + private void Write16Bit(Stream stream, Buffer2D pixels) + where TPixel : struct, IPixel + { + using (IManagedByteBuffer row = this.AllocateRow(pixels.Width, 2)) + { + for (int y = pixels.Height - 1; y >= 0; y--) + { + Span pixelSpan = pixels.GetRowSpan(y); + + PixelOperations.Instance.ToBgra5551Bytes( + this.configuration, + pixelSpan, + row.GetSpan(), + pixelSpan.Length); + + stream.Write(row.Array, 0, row.Length()); + } + } + } } } diff --git a/src/ImageSharp/Formats/Bmp/IBmpEncoderOptions.cs b/src/ImageSharp/Formats/Bmp/IBmpEncoderOptions.cs index a3a056bfa2..96ec423e7b 100644 --- a/src/ImageSharp/Formats/Bmp/IBmpEncoderOptions.cs +++ b/src/ImageSharp/Formats/Bmp/IBmpEncoderOptions.cs @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// /// Configuration options for use during bmp encoding /// - /// The encoder can currently only write 24-bit and 32-bit rgb images to streams. + /// The encoder can currently only write 16-bit, 24-bit and 32-bit rgb images to streams. internal interface IBmpEncoderOptions { /// diff --git a/src/ImageSharp/PixelFormats/IPixel.cs b/src/ImageSharp/PixelFormats/IPixel.cs index 1277406869..21ec2a3fdc 100644 --- a/src/ImageSharp/PixelFormats/IPixel.cs +++ b/src/ImageSharp/PixelFormats/IPixel.cs @@ -61,6 +61,12 @@ namespace SixLabors.ImageSharp.PixelFormats /// The value. void FromArgb32(Argb32 source); + /// + /// Initializes the pixel instance from an value. + /// + /// The value. + void FromBgra5551(Bgra5551 source); + /// /// Initializes the pixel instance from an value. /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Alpha8.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Alpha8.cs index 75b7ede827..cd864b5455 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Alpha8.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Alpha8.cs @@ -87,6 +87,10 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra32(Bgra32 source) => this.PackedValue = source.A; + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromGray8(Gray8 source) => this.PackedValue = byte.MaxValue; diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs index 8fc3016314..075df01cd2 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs @@ -187,6 +187,10 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public void FromArgb32(Argb32 source) => this.PackedValue = source.PackedValue; + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromBgr24(Bgr24 source) diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs index 96ff7da6f6..3ba6436a0b 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs @@ -109,6 +109,10 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public void FromBgr24(Bgr24 source) => this = source; + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra32(Bgra32 source) diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs index a2e4dc8802..9e42de388c 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs @@ -87,6 +87,10 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public void FromArgb32(Argb32 source) => this.FromVector4(source.ToVector4()); + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra5551(Bgra5551 source) => this.FromVector4(source.ToVector4()); + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs index 1d156222ff..758be8043c 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs @@ -159,6 +159,10 @@ namespace SixLabors.ImageSharp.PixelFormats this.A = byte.MaxValue; } + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra32(Bgra32 source) => this = source; diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs index 110b51822d..6fcac62917 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs @@ -90,6 +90,10 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs index dcfb25a64b..abb3eb19e5 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs @@ -87,6 +87,10 @@ namespace SixLabors.ImageSharp.PixelFormats (this.PackedValue >> 15) & 0x01); } + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra5551(Bgra5551 source) => this = source; + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs index 43a03dc5d1..a07cd5213c 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs @@ -111,6 +111,10 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Gray16.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Gray16.cs index 2e98a28ada..6fce1c7575 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Gray16.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Gray16.cs @@ -106,6 +106,10 @@ namespace SixLabors.ImageSharp.PixelFormats ImageMaths.UpscaleFrom8BitTo16Bit(source.B)); } + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromGray8(Gray8 source) => this.PackedValue = ImageMaths.UpscaleFrom8BitTo16Bit(source.PackedValue); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Gray8.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Gray8.cs index 512bee39de..1c278b434c 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Gray8.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Gray8.cs @@ -97,6 +97,10 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra32(Bgra32 source) => this.PackedValue = ImageMaths.Get8BitBT709Luminance(source.R, source.G, source.B); + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromGray8(Gray8 source) => this.PackedValue = source.PackedValue; diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs index 8323cf3e8a..580cc5399b 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs @@ -88,6 +88,10 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromGray8(Gray8 source) => this.FromScaledVector4(source.ToScaledVector4()); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs index cb915459bc..e2ed931b75 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs @@ -99,6 +99,10 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromGray8(Gray8 source) => this.FromScaledVector4(source.ToScaledVector4()); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs index 9f60ca8c77..3b30ebd1e6 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs @@ -107,6 +107,10 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromGray8(Gray8 source) => this.FromScaledVector4(source.ToScaledVector4()); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs index d39cfd402e..cd95e87ecf 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs @@ -8,7 +8,7 @@ using System.Runtime.CompilerServices; namespace SixLabors.ImageSharp.PixelFormats { /// - /// Packed packed pixel type containing two 8-bit signed normalized values, ranging from −1 to 1. + /// Packed pixel type containing two 8-bit signed normalized values, ranging from −1 to 1. /// /// Ranges from [-1, -1, 0, 1] to [1, 1, 0, 1] in vector form. /// @@ -107,10 +107,11 @@ namespace SixLabors.ImageSharp.PixelFormats /// [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) - { - dest.FromScaledVector4(this.ToScaledVector4()); - } + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); /// [MethodImpl(InliningOptions.ShortMethod)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs index 8a9368463f..73a3d32625 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs @@ -110,6 +110,10 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromGray8(Gray8 source) => this.FromScaledVector4(source.ToScaledVector4()); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs index b9cab1e7d3..8d7c400b45 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs @@ -105,6 +105,10 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromGray8(Gray8 source) => this.FromScaledVector4(source.ToScaledVector4()); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs index 3bc74e6c67..453b1c1b7c 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs @@ -112,6 +112,10 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromGray8(Gray8 source) => this.FromScaledVector4(source.ToScaledVector4()); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs index 6dc623518d..0411f8f3e9 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs @@ -93,6 +93,10 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromGray8(Gray8 source) => this.FromScaledVector4(source.ToScaledVector4()); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs index 86565731d2..1255f66d15 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs @@ -156,6 +156,10 @@ namespace SixLabors.ImageSharp.PixelFormats this.B = rgb; } + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromRgb24(Rgb24 source) => this = source; diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs index eda116a46c..f59036ec7d 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs @@ -126,6 +126,10 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public void FromRgba64(Rgba64 source) => this = source.Rgb; + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromGray8(Gray8 source) diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs index 895added1b..38f61d56c1 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs @@ -99,6 +99,10 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromGray8(Gray8 source) => this.FromScaledVector4(source.ToScaledVector4()); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs index 5a16704ef0..7367c44634 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs @@ -266,6 +266,10 @@ namespace SixLabors.ImageSharp.PixelFormats this.A = source.A; } + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromGray8(Gray8 source) diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs index 5ae5492e23..87d1b235bf 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs @@ -9,7 +9,7 @@ using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.PixelFormats { /// - /// Packed pixel type containing four 16-bit unsigned normalized values ranging from 0 to 635535. + /// Packed pixel type containing four 16-bit unsigned normalized values ranging from 0 to 65535. /// /// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form. /// @@ -154,6 +154,10 @@ namespace SixLabors.ImageSharp.PixelFormats this.A = ImageMaths.UpscaleFrom8BitTo16Bit(source.A); } + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromGray8(Gray8 source) diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs index d65a5ade78..96003cc5bf 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs @@ -134,6 +134,10 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromGray8(Gray8 source) => this.FromScaledVector4(source.ToScaledVector4()); @@ -152,10 +156,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) - { - dest.FromScaledVector4(this.ToScaledVector4()); - } + public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); /// [MethodImpl(InliningOptions.ShortMethod)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs index 96fe15ed61..803a77b23f 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs @@ -111,6 +111,10 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromGray8(Gray8 source) => this.FromScaledVector4(source.ToScaledVector4()); @@ -129,10 +133,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) - { - dest.FromScaledVector4(this.ToScaledVector4()); - } + public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); /// [MethodImpl(InliningOptions.ShortMethod)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs index d224f8eb4e..c52b293476 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs @@ -116,6 +116,10 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromGray8(Gray8 source) => this.FromScaledVector4(source.ToScaledVector4()); diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.cs index 207a8767d6..7002819928 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.cs @@ -657,5 +657,77 @@ namespace SixLabors.ImageSharp.PixelFormats { this.ToRgba64(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); } + + /// + /// Converts all pixels in 'source` span of into a span of -s. + /// + /// A to configure internal operations + /// The source of data. + /// The to the destination pixels. + internal virtual void FromBgra5551(Configuration configuration, ReadOnlySpan source, Span destPixels) + { + Guard.DestinationShouldNotBeTooShort(source, destPixels, nameof(destPixels)); + + ref Bgra5551 sourceBaseRef = ref MemoryMarshal.GetReference(source); + ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < source.Length; i++) + { + ref Bgra5551 sp = ref Unsafe.Add(ref sourceBaseRef, i); + ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); + + dp.FromBgra5551(sp); + } + } + + /// + /// A helper for that expects a byte span. + /// The layout of the data in 'sourceBytes' must be compatible with layout. + /// + /// A to configure internal operations + /// The to the source bytes. + /// The to the destination pixels. + /// The number of pixels to convert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void FromBgra5551Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destPixels, int count) + { + this.FromBgra5551(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destPixels); + } + + /// + /// Converts all pixels of the 'sourcePixels` span to a span of -s. + /// + /// A to configure internal operations + /// The span of source pixels + /// The destination span of data. + internal virtual void ToBgra5551(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Bgra5551 destBaseRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); + ref Bgra5551 dp = ref Unsafe.Add(ref destBaseRef, i); + + dp.FromScaledVector4(sp.ToScaledVector4()); + } + } + + /// + /// A helper for that expects a byte span as destination. + /// The layout of the data in 'destBytes' must be compatible with layout. + /// + /// A to configure internal operations + /// The to the source pixels. + /// The to the destination bytes. + /// The number of pixels to convert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void ToBgra5551Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) + { + this.ToBgra5551(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); + } } } \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.tt b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.tt index 8579423b34..8603012321 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.tt @@ -136,5 +136,8 @@ namespace SixLabors.ImageSharp.PixelFormats GenerateFromMethods("Rgba64"); GenerateToDestFormatMethods("Rgba64"); + GenerateFromMethods("Bgra5551"); + GenerateToDestFormatMethods("Bgra5551"); + #> } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index 5f3cc7db47..e615dbe568 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -11,7 +11,7 @@ using Xunit; // ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests.Formats.Bmp { using SixLabors.ImageSharp.Metadata; using static TestImages.Bmp; diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs index c7558ab422..7e054734e3 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs @@ -6,10 +6,11 @@ using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; + using Xunit; using Xunit.Abstractions; -namespace SixLabors.ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests.Formats.Bmp { using static TestImages.Bmp; @@ -25,16 +26,16 @@ namespace SixLabors.ImageSharp.Tests public static readonly TheoryData RatioFiles = new TheoryData { - { TestImages.Bmp.Car, 3780, 3780 , PixelResolutionUnit.PixelsPerMeter }, - { TestImages.Bmp.V5Header, 3780, 3780 , PixelResolutionUnit.PixelsPerMeter }, - { TestImages.Bmp.RLE8, 2835, 2835, PixelResolutionUnit.PixelsPerMeter } + { Car, 3780, 3780 , PixelResolutionUnit.PixelsPerMeter }, + { V5Header, 3780, 3780 , PixelResolutionUnit.PixelsPerMeter }, + { RLE8, 2835, 2835, PixelResolutionUnit.PixelsPerMeter } }; public static readonly TheoryData BmpBitsPerPixelFiles = new TheoryData { - { TestImages.Bmp.Car, BmpBitsPerPixel.Pixel24 }, - { TestImages.Bmp.Bit32Rgb, BmpBitsPerPixel.Pixel32 } + { Car, BmpBitsPerPixel.Pixel24 }, + { Bit32Rgb, BmpBitsPerPixel.Pixel32 } }; public BmpEncoderTests(ITestOutputHelper output) => this.Output = output; @@ -111,6 +112,8 @@ namespace SixLabors.ImageSharp.Tests [WithFile(WinBmpv5, PixelTypes.Rgba32 | PixelTypes.Rgb24, BmpBitsPerPixel.Pixel32)] // WinBmpv3 is a 24 bits per pixel image [WithFile(WinBmpv3, PixelTypes.Rgb24, BmpBitsPerPixel.Pixel24)] + [WithFile(Rgb16, PixelTypes.Bgra5551, BmpBitsPerPixel.Pixel16)] + [WithFile(Bit16, PixelTypes.Bgra5551, BmpBitsPerPixel.Pixel16)] public void Encode_WithV3Header_Works(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) // if supportTransparency is false, a v3 bitmap header will be written where TPixel : struct, IPixel => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: false); @@ -121,6 +124,8 @@ namespace SixLabors.ImageSharp.Tests [WithFile(WinBmpv4, PixelTypes.Rgba32 | PixelTypes.Rgb24, BmpBitsPerPixel.Pixel32)] [WithFile(WinBmpv5, PixelTypes.Rgba32 | PixelTypes.Rgb24, BmpBitsPerPixel.Pixel32)] [WithFile(WinBmpv3, PixelTypes.Rgb24, BmpBitsPerPixel.Pixel24)] + [WithFile(Rgb16, PixelTypes.Bgra5551, BmpBitsPerPixel.Pixel16)] + [WithFile(Bit16, PixelTypes.Bgra5551, BmpBitsPerPixel.Pixel16)] public void Encode_WithV4Header_Works(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) where TPixel : struct, IPixel => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: true); diff --git a/tests/ImageSharp.Tests/PixelFormats/Alpha8Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Alpha8Tests.cs index 8f68c9d03f..b6e87626b6 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Alpha8Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Alpha8Tests.cs @@ -94,5 +94,19 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats input.ToRgba32(ref actual); Assert.Equal(expected, actual); } + + [Fact] + public void Alpha8_FromBgra5551() + { + // arrange + var alpha = default(Alpha8); + byte expected = byte.MaxValue; + + // act + alpha.FromBgra5551(new Bgra5551(0.0f, 0.0f, 0.0f, 1.0f)); + + // assert + Assert.Equal(expected, alpha.PackedValue); + } } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Argb32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Argb32Tests.cs index b9f7414900..4f3394e691 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Argb32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Argb32Tests.cs @@ -60,6 +60,20 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(expected, actual); } + [Fact] + public void Argb32_FromBgra5551() + { + // arrange + var argb = default(Argb32); + uint expected = uint.MaxValue; + + // act + argb.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + + // assert + Assert.Equal(expected, argb.PackedValue); + } + [Fact] public void Argb32_Clamping() { diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs index 2295fbe56f..f145e6928e 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs @@ -95,5 +95,20 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(Vec(1, 2, 3), rgb.ToVector4()); } + + [Fact] + public void Bgr24_FromBgra5551() + { + // arrange + var bgr = default(Bgr24); + + // act + bgr.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + + // assert + Assert.Equal(255, bgr.R); + Assert.Equal(255, bgr.G); + Assert.Equal(255, bgr.B); + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs index 967e358e1e..4419fe898a 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs @@ -63,6 +63,20 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(expected, actual); } + [Fact] + public void Bgr565_FromBgra5551() + { + // arrange + var bgr = default(Bgr565); + ushort expected = ushort.MaxValue; + + // act + bgr.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + + // assert + Assert.Equal(expected, bgr.PackedValue); + } + [Fact] public void Bgr565_Clamping() { diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs index a5c53ed8b0..171a3dfa00 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs @@ -102,5 +102,19 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(Vec(1, 2, 3, 4), rgb.ToVector4()); } + + [Fact] + public void Bgra32_FromBgra5551() + { + // arrange + var bgra = default(Bgra32); + uint expected = uint.MaxValue; + + // act + bgra.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + + // assert + Assert.Equal(expected, bgra.PackedValue); + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgra4444Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgra4444Tests.cs index 8b56ec19fe..b6019016c8 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgra4444Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgra4444Tests.cs @@ -64,6 +64,20 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(expected, actual); } + [Fact] + public void Bgra4444_FromBgra5551() + { + // arrange + var bgra = default(Bgra4444); + ushort expected = ushort.MaxValue; + + // act + bgra.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + + // assert + Assert.Equal(expected, bgra.PackedValue); + } + [Fact] public void Bgra4444_Clamping() { diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgra5551Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgra5551Tests.cs index 76edee8a73..f5abca59a1 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgra5551Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgra5551Tests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; using System.Numerics; using SixLabors.ImageSharp.PixelFormats; using Xunit; @@ -18,6 +19,13 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats float w = 0x1; Assert.Equal(0xeacd, new Bgra5551(x / 0x1f, y / 0x1f, z / 0x1f, w).PackedValue); Assert.Equal(3088, new Bgra5551(0.1f, -0.3f, 0.5f, -0.7f).PackedValue); + + Assert.Equal(0xFFFF, new Bgra5551(Vector4.One).PackedValue); + Assert.Equal(0x7C00, new Bgra5551(Vector4.UnitX).PackedValue); + Assert.Equal(0x03E0, new Bgra5551(Vector4.UnitY).PackedValue); + Assert.Equal(0x001F, new Bgra5551(Vector4.UnitZ).PackedValue); + Assert.Equal(0x8000, new Bgra5551(Vector4.UnitW).PackedValue); + // Test the limits. Assert.Equal(0x0, new Bgra5551(Vector4.Zero).PackedValue); Assert.Equal(0xFFFF, new Bgra5551(Vector4.One).PackedValue); @@ -53,7 +61,6 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Vector4 scaled = new Bgra5551(Vector4.One).ToScaledVector4(); int expected = 0xFFFF; var pixel = default(Bgra5551); - pixel.FromScaledVector4(scaled); // act pixel.FromScaledVector4(scaled); @@ -63,6 +70,126 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(expected, actual); } + [Fact] + public void Bgra5551_FromRgba32() + { + // arrange + var bgra1 = default(Bgra5551); + var bgra2 = default(Bgra5551); + ushort expectedPackedValue1 = ushort.MaxValue; + ushort expectedPackedValue2 = 0xFC1F; + + // act + bgra1.FromRgba32(new Rgba32(255, 255, 255, 255)); + bgra2.FromRgba32(new Rgba32(255, 0, 255, 255)); + + // assert + Assert.Equal(expectedPackedValue1, bgra1.PackedValue); + Assert.Equal(expectedPackedValue2, bgra2.PackedValue); + } + + [Fact] + public void Bgra5551_FromBgra32() + { + // arrange + var bgra1 = default(Bgra5551); + var bgra2 = default(Bgra5551); + ushort expectedPackedValue1 = ushort.MaxValue; + ushort expectedPackedValue2 = 0xFC1F; + + // act + bgra1.FromBgra32(new Bgra32(255, 255, 255, 255)); + bgra2.FromBgra32(new Bgra32(255, 0, 255, 255)); + + // assert + Assert.Equal(expectedPackedValue1, bgra1.PackedValue); + Assert.Equal(expectedPackedValue2, bgra2.PackedValue); + } + + [Fact] + public void Bgra5551_FromArgb32() + { + // arrange + var bgra = default(Bgra5551); + ushort expectedPackedValue = ushort.MaxValue; + + // act + bgra.FromArgb32(new Argb32(255, 255, 255, 255)); + + // assert + Assert.Equal(expectedPackedValue, bgra.PackedValue); + } + + [Fact] + public void Bgra5551_FromRgba64() + { + // arrange + var bgra = default(Bgra5551); + ushort expectedPackedValue = ushort.MaxValue; + + // act + bgra.FromRgba64(new Rgba64(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)); + + // assert + Assert.Equal(expectedPackedValue, bgra.PackedValue); + } + + [Fact] + public void Bgra5551_FromGrey16() + { + // arrange + var bgra = default(Bgra5551); + ushort expectedPackedValue = ushort.MaxValue; + + // act + bgra.FromGray16(new Gray16(ushort.MaxValue)); + + // assert + Assert.Equal(expectedPackedValue, bgra.PackedValue); + } + + [Fact] + public void Bgra5551_FromGrey8() + { + // arrange + var bgra = default(Bgra5551); + ushort expectedPackedValue = ushort.MaxValue; + + // act + bgra.FromGray8(new Gray8(byte.MaxValue)); + + // assert + Assert.Equal(expectedPackedValue, bgra.PackedValue); + } + + [Fact] + public void Bgra5551_FromBgr24() + { + // arrange + var bgra = default(Bgra5551); + ushort expectedPackedValue = ushort.MaxValue; + + // act + bgra.FromBgr24(new Bgr24(byte.MaxValue, byte.MaxValue, byte.MaxValue)); + + // assert + Assert.Equal(expectedPackedValue, bgra.PackedValue); + } + + [Fact] + public void Bgra5551_FromRgb24() + { + // arrange + var bgra = default(Bgra5551); + ushort expectedPackedValue = ushort.MaxValue; + + // act + bgra.FromRgb24(new Rgb24(byte.MaxValue, byte.MaxValue, byte.MaxValue)); + + // assert + Assert.Equal(expectedPackedValue, bgra.PackedValue); + } + [Fact] public void Bgra5551_Clamping() { diff --git a/tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs index 8391ef25ae..9174a6abdb 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs @@ -61,6 +61,20 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(expected, actual); } + [Fact] + public void Byte4_FromBgra5551() + { + // arrange + var rgb = default(Byte4); + uint expected = 0xFFFFFFFF; + + // act + rgb.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + + // assert + Assert.Equal(expected, rgb.PackedValue); + } + [Fact] public void Byte4_Clamping() { diff --git a/tests/ImageSharp.Tests/PixelFormats/Gray16Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Gray16Tests.cs index cb19c031d0..01b60832e2 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Gray16Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Gray16Tests.cs @@ -126,5 +126,19 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(expected, actual.B); Assert.Equal(byte.MaxValue, actual.A); } + + [Fact] + public void Gray16_FromBgra5551() + { + // arrange + var gray = default(Gray16); + ushort expected = ushort.MaxValue; + + // act + gray.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + + // assert + Assert.Equal(expected, gray.PackedValue); + } } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Gray8Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Gray8Tests.cs index 1e17985e60..159f97cdee 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Gray8Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Gray8Tests.cs @@ -148,6 +148,20 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(byte.MaxValue, actual.A); } + [Fact] + public void Gray8_FromBgra5551() + { + // arrange + var grey = default(Gray8); + byte expected = byte.MaxValue; + + // act + grey.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + + // assert + Assert.Equal(expected, grey.PackedValue); + } + public class Rgba32Compatibility { // ReSharper disable once MemberHidesStaticFromOuterClass diff --git a/tests/ImageSharp.Tests/PixelFormats/HalfVector2Tests.cs b/tests/ImageSharp.Tests/PixelFormats/HalfVector2Tests.cs index ccdd23e8fb..57da5438ca 100644 --- a/tests/ImageSharp.Tests/PixelFormats/HalfVector2Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/HalfVector2Tests.cs @@ -71,5 +71,22 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats // assert Assert.Equal(expected, actual); } + + [Fact] + public void HalfVector2_FromBgra5551() + { + // arrange + var halfVector2 = default(HalfVector2); + + // act + halfVector2.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + + // assert + Vector4 actual = halfVector2.ToScaledVector4(); + Assert.Equal(1F, actual.X); + Assert.Equal(1F, actual.Y); + Assert.Equal(0, actual.Z); + Assert.Equal(1, actual.W); + } } } diff --git a/tests/ImageSharp.Tests/PixelFormats/HalfVector4Tests.cs b/tests/ImageSharp.Tests/PixelFormats/HalfVector4Tests.cs index c61dd97d2a..ed1a0b7209 100644 --- a/tests/ImageSharp.Tests/PixelFormats/HalfVector4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/HalfVector4Tests.cs @@ -65,5 +65,19 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats // assert Assert.Equal(expected, actual); } + + [Fact] + public void HalfVector4_FromBgra5551() + { + // arrange + var halfVector4 = default(HalfVector4); + Vector4 expected = Vector4.One; + + // act + halfVector4.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + + // assert + Assert.Equal(expected, halfVector4.ToScaledVector4()); + } } } diff --git a/tests/ImageSharp.Tests/PixelFormats/NormalizedByte2Tests.cs b/tests/ImageSharp.Tests/PixelFormats/NormalizedByte2Tests.cs index 506ebe0fe0..1533f9cf9a 100644 --- a/tests/ImageSharp.Tests/PixelFormats/NormalizedByte2Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/NormalizedByte2Tests.cs @@ -66,5 +66,19 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats // assert Assert.Equal(expected, actual); } + + [Fact] + public void NormalizedByte2_FromBgra5551() + { + // arrange + var normalizedByte2 = default(NormalizedByte2); + var expected = new Vector4(1, 1, 0, 1); + + // act + normalizedByte2.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + + // assert + Assert.Equal(expected, normalizedByte2.ToVector4()); + } } } diff --git a/tests/ImageSharp.Tests/PixelFormats/NormalizedByte4Tests.cs b/tests/ImageSharp.Tests/PixelFormats/NormalizedByte4Tests.cs index 19a49e5d8a..1cb404a008 100644 --- a/tests/ImageSharp.Tests/PixelFormats/NormalizedByte4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/NormalizedByte4Tests.cs @@ -60,5 +60,19 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats // assert Assert.Equal(expected, actual); } + + [Fact] + public void NormalizedByte4_FromBgra5551() + { + // arrange + var normalizedByte4 = default(NormalizedByte4); + Vector4 expected = Vector4.One; + + // act + normalizedByte4.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + + // assert + Assert.Equal(expected, normalizedByte4.ToVector4()); + } } } diff --git a/tests/ImageSharp.Tests/PixelFormats/NormalizedShort2Tests.cs b/tests/ImageSharp.Tests/PixelFormats/NormalizedShort2Tests.cs index 216ed4ad75..ff9350b701 100644 --- a/tests/ImageSharp.Tests/PixelFormats/NormalizedShort2Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/NormalizedShort2Tests.cs @@ -69,5 +69,19 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats // assert Assert.Equal(expected, actual); } + + [Fact] + public void NormalizedShort2_FromBgra5551() + { + // arrange + var normalizedShort2 = default(NormalizedShort2); + var expected = new Vector4(1, 1, 0, 1); + + // act + normalizedShort2.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + + // assert + Assert.Equal(expected, normalizedShort2.ToVector4()); + } } } diff --git a/tests/ImageSharp.Tests/PixelFormats/NormalizedShort4Tests.cs b/tests/ImageSharp.Tests/PixelFormats/NormalizedShort4Tests.cs index d06d46d06f..cea7e3146e 100644 --- a/tests/ImageSharp.Tests/PixelFormats/NormalizedShort4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/NormalizedShort4Tests.cs @@ -61,5 +61,19 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats // assert Assert.Equal(expected, actual); } + + [Fact] + public void NormalizedShort4_FromBgra5551() + { + // arrange + var normalizedShort4 = default(NormalizedShort4); + Vector4 expected = Vector4.One; + + // act + normalizedShort4.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + + // assert + Assert.Equal(expected, normalizedShort4.ToVector4()); + } } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Rg32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rg32Tests.cs index 46e5fbc3cd..bccaaf8168 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rg32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rg32Tests.cs @@ -62,6 +62,20 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(expected, actual); } + [Fact] + public void Rg32_FromBgra5551() + { + // arrange + var rg32 = new Rg32(Vector2.One); + uint expected = 0xFFFFFFFF; + + // act + rg32.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + + // assert + Assert.Equal(expected, rg32.PackedValue); + } + [Fact] public void Rg32_Clamping() { diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs index 92e8d302d4..df459422c9 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs @@ -103,11 +103,31 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats [Fact] public void ToRgba32() { + // arrange var rgb = new Rgb24(1, 2, 3); Rgba32 rgba = default; + var expected = new Rgba32(1, 2, 3, 255); + + // act rgb.ToRgba32(ref rgba); - Assert.Equal(new Rgba32(1, 2, 3, 255), rgba); + // assert + Assert.Equal(expected, rgba); + } + + [Fact] + public void Rgb24_FromBgra5551() + { + // arrange + var rgb = new Rgb24(255, 255, 255); + + // act + rgb.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + + // assert + Assert.Equal(255, rgb.R); + Assert.Equal(255, rgb.G); + Assert.Equal(255, rgb.B); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgb48Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgb48Tests.cs index d30e498609..3bddc21ab3 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgb48Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgb48Tests.cs @@ -58,5 +58,21 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats // assert Assert.Equal(expected, actual); } + + [Fact] + public void Rgb48_FromBgra5551() + { + // arrange + var rgb = default(Rgb48); + ushort expected = ushort.MaxValue; + + // act + rgb.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + + // assert + Assert.Equal(expected, rgb.R); + Assert.Equal(expected, rgb.G); + Assert.Equal(expected, rgb.B); + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs index a897dd4cdb..0dbed2d2cb 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs @@ -64,6 +64,20 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(expected, actual.PackedValue); } + [Fact] + public void Rgba1010102_FromBgra5551() + { + // arrange + var rgba = new Rgba1010102(Vector4.One); + uint expected = 0xFFFFFFFF; + + // act + rgba.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + + // assert + Assert.Equal(expected, rgba.PackedValue); + } + [Fact] public void Rgba1010102_Clamping() { diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs index ad1d137406..275afa35d6 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs @@ -276,5 +276,19 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats // assert Assert.Equal(expected, actual); } + + [Fact] + public void Rgba32_FromBgra5551() + { + // arrange + var rgb = default(Rgba32); + uint expected = 0xFFFFFFFF; + + // act + rgb.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + + // assert + Assert.Equal(expected, rgb.PackedValue); + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs index 3e5d7a56ed..51f80e0e18 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs @@ -91,5 +91,22 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats // assert Assert.Equal(expected, actual); } + + [Fact] + public void Rgba64_FromBgra5551() + { + // arrange + var rgba = default(Rgba64); + ushort expected = ushort.MaxValue; + + // act + rgba.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + + // assert + Assert.Equal(expected, rgba.R); + Assert.Equal(expected, rgba.G); + Assert.Equal(expected, rgba.B); + Assert.Equal(expected, rgba.A); + } } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Short2Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Short2Tests.cs index c9a3b33c9a..4ae172ed4e 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Short2Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Short2Tests.cs @@ -141,5 +141,22 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats // assert Assert.Equal(expected, actual); } + + [Fact] + public void Short2_FromBgra5551() + { + // arrange + var short2 = default(Short2); + + // act + short2.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + + // assert + Vector4 actual = short2.ToScaledVector4(); + Assert.Equal(1, actual.X); + Assert.Equal(1, actual.Y); + Assert.Equal(0, actual.Z); + Assert.Equal(1, actual.W); + } } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Short4Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Short4Tests.cs index 247342a053..1cb7d89980 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Short4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Short4Tests.cs @@ -181,5 +181,19 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats // assert Assert.Equal(expected, actual); } + + [Fact] + public void Short4_FromBgra5551() + { + // arrange + var short4 = default(Short4); + Vector4 expected = Vector4.One; + + // act + short4.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + + // assert + Assert.Equal(expected, short4.ToScaledVector4()); + } } } diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 23bebd6213..f82278ef5d 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -241,6 +241,7 @@ namespace SixLabors.ImageSharp.Tests public const string Bit16Inverted = "Bmp/test16-inverted.bmp"; public const string Bit32Rgb = "Bmp/rgb32.bmp"; public const string Bit32Rgba = "Bmp/rgba32.bmp"; + public const string Rgb16 = "Bmp/rgb16.bmp"; // Note: This format can be called OS/2 BMPv1, or Windows BMPv2 public const string WinBmpv2 = "Bmp/pal8os2v1_winv2.bmp"; diff --git a/tests/ImageSharp.Tests/TestUtilities/PixelTypes.cs b/tests/ImageSharp.Tests/TestUtilities/PixelTypes.cs index a051e577db..e4a7572d60 100644 --- a/tests/ImageSharp.Tests/TestUtilities/PixelTypes.cs +++ b/tests/ImageSharp.Tests/TestUtilities/PixelTypes.cs @@ -58,6 +58,8 @@ namespace SixLabors.ImageSharp.Tests Rgb48 = 1 << 21, + Bgra5551 = 1 << 22, + // TODO: Add multi-flag entries by rules defined in PackedPixelConverterHelper // "All" is handled as a separate, individual case instead of using bitwise OR diff --git a/tests/Images/Input/Bmp/rgb16.bmp b/tests/Images/Input/Bmp/rgb16.bmp new file mode 100644 index 0000000000..fde234fbb5 --- /dev/null +++ b/tests/Images/Input/Bmp/rgb16.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fcecd482048c853d08255430c5bac462821522683f4429626442580058aa5e5e +size 16438 From 2fa57ff510284c657a8ff87e369a30e5cfdaf734 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Wed, 1 May 2019 17:41:36 +0200 Subject: [PATCH 100/223] define IImageVisitor --- src/ImageSharp/IImage.cs | 2 +- src/ImageSharp/Image.cs | 40 +++++++++++++++++++ src/ImageSharp/Image{TPixel}.cs | 5 +++ .../IImageProcessingContext{TPixel}.cs | 5 +++ .../Processing/ProcessingExtensions.cs | 5 +++ .../Processing/Processors/IImageProcessor.cs | 6 +++ 6 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 src/ImageSharp/Image.cs diff --git a/src/ImageSharp/IImage.cs b/src/ImageSharp/IImage.cs index b9e2cee616..fb251cfd5a 100644 --- a/src/ImageSharp/IImage.cs +++ b/src/ImageSharp/IImage.cs @@ -4,7 +4,7 @@ using System; namespace SixLabors.ImageSharp -{ +{ /// /// Encapsulates the properties and methods that describe an image. /// diff --git a/src/ImageSharp/Image.cs b/src/ImageSharp/Image.cs new file mode 100644 index 0000000000..6bea675b9f --- /dev/null +++ b/src/ImageSharp/Image.cs @@ -0,0 +1,40 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Metadata; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp +{ + internal interface IImageVisitor + { + void Visit(Image image) + where TPixel : struct, IPixel; + } + + public abstract partial class Image : IImage + { + /// + public PixelTypeInfo PixelType { get; } + + /// + public abstract int Width { get; } + + /// + public abstract int Height { get; } + + /// + public ImageMetadata Metadata { get; } + + protected Image(PixelTypeInfo pixelType, ImageMetadata metadata) + { + this.PixelType = pixelType; + this.Metadata = metadata ?? new ImageMetadata(); + } + + public abstract void Dispose(); + + internal abstract void ApplyVisitor(IImageVisitor visitor); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Image{TPixel}.cs b/src/ImageSharp/Image{TPixel}.cs index f2bef78e1a..2cf389d37e 100644 --- a/src/ImageSharp/Image{TPixel}.cs +++ b/src/ImageSharp/Image{TPixel}.cs @@ -220,6 +220,11 @@ namespace SixLabors.ImageSharp /// public void Dispose() => this.Frames.Dispose(); + internal override void ApplyVisitor(IImageVisitor visitor) + { + visitor.Visit(this); + } + /// public override string ToString() => $"Image<{typeof(TPixel).Name}>: {this.Width}x{this.Height}"; diff --git a/src/ImageSharp/Processing/IImageProcessingContext{TPixel}.cs b/src/ImageSharp/Processing/IImageProcessingContext{TPixel}.cs index 4897cc58b4..72b78b32ac 100644 --- a/src/ImageSharp/Processing/IImageProcessingContext{TPixel}.cs +++ b/src/ImageSharp/Processing/IImageProcessingContext{TPixel}.cs @@ -8,6 +8,11 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { + public interface IImageProcessingContext + { + + } + /// /// An interface to queue up image operations to apply to an image. /// diff --git a/src/ImageSharp/Processing/ProcessingExtensions.cs b/src/ImageSharp/Processing/ProcessingExtensions.cs index 9d06c61d4c..c96b971117 100644 --- a/src/ImageSharp/Processing/ProcessingExtensions.cs +++ b/src/ImageSharp/Processing/ProcessingExtensions.cs @@ -13,6 +13,11 @@ namespace SixLabors.ImageSharp.Processing /// public static class ProcessingExtensions { + public static void Mutate(this Image source, Action operation) + { + + } + /// /// Applies the given operation to the mutable image. /// Useful when we need to extract information like Width/Height to parametrize the next operation working on the chain. diff --git a/src/ImageSharp/Processing/Processors/IImageProcessor.cs b/src/ImageSharp/Processing/Processors/IImageProcessor.cs index d7fe0465be..78800affbc 100644 --- a/src/ImageSharp/Processing/Processors/IImageProcessor.cs +++ b/src/ImageSharp/Processing/Processors/IImageProcessor.cs @@ -6,6 +6,12 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors { + public interface IImageProcessor + { + IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel; + } + /// /// Encapsulates methods to alter the pixels of an image. /// From 0d2c0e78c8b4bf873484aba3caa283c955df4cc0 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Wed, 1 May 2019 21:26:35 +0200 Subject: [PATCH 101/223] pixel agnostic Mutate/Clone defined --- src/ImageSharp/Image.cs | 15 ++++++-- src/ImageSharp/Image{TPixel}.cs | 19 ++++------- .../IImageProcessingContext{TPixel}.cs | 20 +++++------ .../Processing/ProcessingExtensions.cs | 34 ++++++++++++++++++- 4 files changed, 60 insertions(+), 28 deletions(-) diff --git a/src/ImageSharp/Image.cs b/src/ImageSharp/Image.cs index 6bea675b9f..00a9a20755 100644 --- a/src/ImageSharp/Image.cs +++ b/src/ImageSharp/Image.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; @@ -13,8 +14,10 @@ namespace SixLabors.ImageSharp where TPixel : struct, IPixel; } - public abstract partial class Image : IImage + public abstract partial class Image : IImage, IConfigurable { + protected readonly Configuration configuration; + /// public PixelTypeInfo PixelType { get; } @@ -27,14 +30,20 @@ namespace SixLabors.ImageSharp /// public ImageMetadata Metadata { get; } - protected Image(PixelTypeInfo pixelType, ImageMetadata metadata) + /// + /// Gets the pixel buffer. + /// + Configuration IConfigurable.Configuration => this.configuration; + + protected Image(Configuration configuration, PixelTypeInfo pixelType, ImageMetadata metadata) { + this.configuration = configuration ?? Configuration.Default; this.PixelType = pixelType; this.Metadata = metadata ?? new ImageMetadata(); } public abstract void Dispose(); - internal abstract void ApplyVisitor(IImageVisitor visitor); + internal abstract void AcceptVisitor(IImageVisitor visitor); } } \ No newline at end of file diff --git a/src/ImageSharp/Image{TPixel}.cs b/src/ImageSharp/Image{TPixel}.cs index 2cf389d37e..64ec87c4dd 100644 --- a/src/ImageSharp/Image{TPixel}.cs +++ b/src/ImageSharp/Image{TPixel}.cs @@ -18,11 +18,9 @@ namespace SixLabors.ImageSharp /// Encapsulates an image, which consists of the pixel data for a graphics image and its attributes. /// /// The pixel format. - public sealed class Image : IImage, IConfigurable + public sealed class Image : Image where TPixel : struct, IPixel { - private readonly Configuration configuration; - /// /// Initializes a new instance of the class /// with the height and the width of the image. @@ -68,8 +66,8 @@ namespace SixLabors.ImageSharp /// The height of the image in pixels. /// The images metadata. internal Image(Configuration configuration, int width, int height, ImageMetadata metadata) + : base(configuration, PixelTypeInfo.Create(), metadata) { - this.configuration = configuration ?? Configuration.Default; this.PixelType = new PixelTypeInfo(Unsafe.SizeOf() * 8); this.Metadata = metadata ?? new ImageMetadata(); this.Frames = new ImageFrameCollection(this, width, height, default(TPixel)); @@ -85,8 +83,8 @@ namespace SixLabors.ImageSharp /// The height of the image in pixels. /// The images metadata. internal Image(Configuration configuration, MemorySource memorySource, int width, int height, ImageMetadata metadata) + : base(configuration, PixelTypeInfo.Create(), metadata) { - this.configuration = configuration; this.PixelType = new PixelTypeInfo(Unsafe.SizeOf() * 8); this.Metadata = metadata; this.Frames = new ImageFrameCollection(this, width, height, memorySource); @@ -102,8 +100,8 @@ namespace SixLabors.ImageSharp /// The color to initialize the pixels with. /// The images metadata. internal Image(Configuration configuration, int width, int height, TPixel backgroundColor, ImageMetadata metadata) + : base(configuration, PixelTypeInfo.Create(), metadata) { - this.configuration = configuration ?? Configuration.Default; this.PixelType = new PixelTypeInfo(Unsafe.SizeOf() * 8); this.Metadata = metadata ?? new ImageMetadata(); this.Frames = new ImageFrameCollection(this, width, height, backgroundColor); @@ -117,18 +115,14 @@ namespace SixLabors.ImageSharp /// The images metadata. /// The frames that will be owned by this image instance. internal Image(Configuration configuration, ImageMetadata metadata, IEnumerable> frames) + : base(configuration,PixelTypeInfo.Create(), metadata) { - this.configuration = configuration ?? Configuration.Default; this.PixelType = new PixelTypeInfo(Unsafe.SizeOf() * 8); this.Metadata = metadata ?? new ImageMetadata(); this.Frames = new ImageFrameCollection(this, frames); } - /// - /// Gets the pixel buffer. - /// - Configuration IConfigurable.Configuration => this.configuration; /// public PixelTypeInfo PixelType { get; } @@ -142,7 +136,6 @@ namespace SixLabors.ImageSharp /// public ImageMetadata Metadata { get; } - /// /// Gets the frames. /// public ImageFrameCollection Frames { get; } @@ -220,7 +213,7 @@ namespace SixLabors.ImageSharp /// public void Dispose() => this.Frames.Dispose(); - internal override void ApplyVisitor(IImageVisitor visitor) + internal override void AcceptVisitor(IImageVisitor visitor) { visitor.Visit(this); } diff --git a/src/ImageSharp/Processing/IImageProcessingContext{TPixel}.cs b/src/ImageSharp/Processing/IImageProcessingContext{TPixel}.cs index 72b78b32ac..1b4fbd3be5 100644 --- a/src/ImageSharp/Processing/IImageProcessingContext{TPixel}.cs +++ b/src/ImageSharp/Processing/IImageProcessingContext{TPixel}.cs @@ -9,16 +9,6 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { public interface IImageProcessingContext - { - - } - - /// - /// An interface to queue up image operations to apply to an image. - /// - /// The pixel format - public interface IImageProcessingContext - where TPixel : struct, IPixel { /// /// Gets a reference to the used to allocate buffers @@ -31,7 +21,15 @@ namespace SixLabors.ImageSharp.Processing /// /// The Size GetCurrentSize(); - + } + + /// + /// An interface to queue up image operations to apply to an image. + /// + /// The pixel format + public interface IImageProcessingContext : IImageProcessingContext + where TPixel : struct, IPixel + { /// /// Adds the processor to the current set of image operations to be applied. /// diff --git a/src/ImageSharp/Processing/ProcessingExtensions.cs b/src/ImageSharp/Processing/ProcessingExtensions.cs index c96b971117..a1acf3aa0b 100644 --- a/src/ImageSharp/Processing/ProcessingExtensions.cs +++ b/src/ImageSharp/Processing/ProcessingExtensions.cs @@ -13,9 +13,41 @@ namespace SixLabors.ImageSharp.Processing /// public static class ProcessingExtensions { - public static void Mutate(this Image source, Action operation) + class ProcessingVisitor : IImageVisitor { + private readonly Action operation; + + private readonly bool mutate; + public Image ResultImage { get; private set; } + + public ProcessingVisitor(Action operation, bool mutate) + { + this.operation = operation; + this.mutate = mutate; + } + + public void Visit(Image image) + where TPixel : struct, IPixel + { + IInternalImageProcessingContext operationsRunner = image.GetConfiguration() + .ImageOperationsProvider.CreateImageProcessingContext(image, this.mutate); + this.operation(operationsRunner); + this.ResultImage = operationsRunner.Apply(); + } + } + + public static void Mutate(this Image source, Action operation) + { + ProcessingVisitor visitor = new ProcessingVisitor(operation, true); + source.AcceptVisitor(visitor); + } + + public static Image Clone(this Image source, Action operation) + { + ProcessingVisitor visitor = new ProcessingVisitor(operation, false); + source.AcceptVisitor(visitor); + return visitor.ResultImage; } /// From 7fae100254b9e81823112fa201b3059b8b7be41a Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Wed, 1 May 2019 21:56:21 +0200 Subject: [PATCH 102/223] pixel-agnostic ResizeProcessor --- .../DefaultInternalImageProcessorContext.cs | 12 + .../IImageProcessingContext{TPixel}.cs | 6 +- src/ImageSharp/Processing/PadExtensions.cs | 3 +- .../Transforms/Resize/ResizeProcessor.cs | 260 ++++-------------- .../Resize/ResizeProcessorImplementation.cs | 188 +++++++++++++ src/ImageSharp/Processing/ResizeExtensions.cs | 40 +-- .../FakeImageOperationsProvider.cs | 10 + .../Image/ImageProcessingContextTests.cs | 74 ++--- 8 files changed, 326 insertions(+), 267 deletions(-) create mode 100644 src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessorImplementation.cs diff --git a/src/ImageSharp/Processing/DefaultInternalImageProcessorContext.cs b/src/ImageSharp/Processing/DefaultInternalImageProcessorContext.cs index 43ba259725..091da003a2 100644 --- a/src/ImageSharp/Processing/DefaultInternalImageProcessorContext.cs +++ b/src/ImageSharp/Processing/DefaultInternalImageProcessorContext.cs @@ -53,6 +53,18 @@ namespace SixLabors.ImageSharp.Processing /// public Size GetCurrentSize() => this.GetCurrentBounds().Size; + public IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle) + { + var processorImplementation = processor.CreatePixelSpecificProcessor(); + return this.ApplyProcessor(processorImplementation, rectangle); + } + + public IImageProcessingContext ApplyProcessor(IImageProcessor processor) + { + var processorImplementation = processor.CreatePixelSpecificProcessor(); + return this.ApplyProcessor(processorImplementation); + } + /// public IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle) { diff --git a/src/ImageSharp/Processing/IImageProcessingContext{TPixel}.cs b/src/ImageSharp/Processing/IImageProcessingContext{TPixel}.cs index 1b4fbd3be5..fefa973f7c 100644 --- a/src/ImageSharp/Processing/IImageProcessingContext{TPixel}.cs +++ b/src/ImageSharp/Processing/IImageProcessingContext{TPixel}.cs @@ -21,8 +21,12 @@ namespace SixLabors.ImageSharp.Processing /// /// The Size GetCurrentSize(); + + IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle); + + IImageProcessingContext ApplyProcessor(IImageProcessor processor); } - + /// /// An interface to queue up image operations to apply to an image. /// diff --git a/src/ImageSharp/Processing/PadExtensions.cs b/src/ImageSharp/Processing/PadExtensions.cs index f730339686..2db219795f 100644 --- a/src/ImageSharp/Processing/PadExtensions.cs +++ b/src/ImageSharp/Processing/PadExtensions.cs @@ -19,8 +19,7 @@ namespace SixLabors.ImageSharp.Processing /// The new width. /// The new height. /// The . - public static IImageProcessingContext Pad(this IImageProcessingContext source, int width, int height) - where TPixel : struct, IPixel + public static IImageProcessingContext Pad(this IImageProcessingContext source, int width, int height) { var options = new ResizeOptions { diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs index e75f6014ab..4348ecdaba 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs @@ -1,98 +1,40 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. +// // Copyright (c) Six Labors and contributors. +// // Licensed under the Apache License, Version 2.0. using System; -using System.Buffers; -using System.Collections.Generic; -using System.Linq; -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { - /// - /// Provides methods that allow the resizing of images using various algorithms. - /// Adapted from - /// - /// The pixel format. - internal class ResizeProcessor : TransformProcessorBase - where TPixel : struct, IPixel + public class ResizeProcessor : IImageProcessor { - // The following fields are not immutable but are optionally created on demand. - private ResizeKernelMap horizontalKernelMap; - private ResizeKernelMap verticalKernelMap; - /// - /// Initializes a new instance of the class. + /// Gets the sampler to perform the resize operation. /// - /// The resize options - /// The source image size - public ResizeProcessor(ResizeOptions options, Size sourceSize) - { - Guard.NotNull(options, nameof(options)); - Guard.NotNull(options.Sampler, nameof(options.Sampler)); - - int targetWidth = options.Size.Width; - int targetHeight = options.Size.Height; - - // Ensure size is populated across both dimensions. - // These dimensions are used to calculate the final dimensions determined by the mode algorithm. - // If only one of the incoming dimensions is 0, it will be modified here to maintain aspect ratio. - // If it is not possible to keep aspect ratio, make sure at least the minimum is is kept. - const int min = 1; - if (targetWidth == 0 && targetHeight > 0) - { - targetWidth = (int)MathF.Max(min, MathF.Round(sourceSize.Width * targetHeight / (float)sourceSize.Height)); - } - - if (targetHeight == 0 && targetWidth > 0) - { - targetHeight = (int)MathF.Max(min, MathF.Round(sourceSize.Height * targetWidth / (float)sourceSize.Width)); - } - - Guard.MustBeGreaterThan(targetWidth, 0, nameof(targetWidth)); - Guard.MustBeGreaterThan(targetHeight, 0, nameof(targetHeight)); + public IResampler Sampler { get; } - (Size size, Rectangle rectangle) = ResizeHelper.CalculateTargetLocationAndBounds(sourceSize, options, targetWidth, targetHeight); + /// + /// Gets the target width. + /// + public int Width { get; } - this.Sampler = options.Sampler; - this.Width = size.Width; - this.Height = size.Height; - this.TargetRectangle = rectangle; - this.Compand = options.Compand; - } + /// + /// Gets the target height. + /// + public int Height { get; } /// - /// Initializes a new instance of the class. + /// Gets the resize rectangle. /// - /// The sampler to perform the resize operation. - /// The target width. - /// The target height. - /// The source image size - public ResizeProcessor(IResampler sampler, int width, int height, Size sourceSize) - : this(sampler, width, height, sourceSize, new Rectangle(0, 0, width, height), false) - { - } + public Rectangle TargetRectangle { get; } /// - /// Initializes a new instance of the class. + /// Gets a value indicating whether to compress or expand individual pixel color values on processing. /// - /// The sampler to perform the resize operation. - /// The target width. - /// The target height. - /// The source image size - /// - /// The structure that specifies the portion of the target image object to draw to. - /// - /// Whether to compress or expand individual pixel color values on processing. + public bool Compand { get; } + public ResizeProcessor(IResampler sampler, int width, int height, Size sourceSize, Rectangle targetRectangle, bool compand) { Guard.NotNull(sampler, nameof(sampler)); @@ -122,149 +64,63 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms this.TargetRectangle = targetRectangle; this.Compand = compand; } - - /// - /// Gets the sampler to perform the resize operation. - /// - public IResampler Sampler { get; } - - /// - /// Gets the target width. - /// - public int Width { get; } - + /// - /// Gets the target height. + /// Initializes a new instance of the class. /// - public int Height { get; } - - /// - /// Gets the resize rectangle. - /// - public Rectangle TargetRectangle { get; } - - /// - /// Gets a value indicating whether to compress or expand individual pixel color values on processing. - /// - public bool Compand { get; } - - /// - protected override Image CreateDestination(Image source, Rectangle sourceRectangle) - { - // We will always be creating the clone even for mutate because we may need to resize the canvas - IEnumerable> frames = source.Frames.Select(x => new ImageFrame(source.GetConfiguration(), this.Width, this.Height, x.Metadata.DeepClone())); - - // Use the overload to prevent an extra frame being added - return new Image(source.GetConfiguration(), source.Metadata.DeepClone(), frames); - } - - /// - protected override void BeforeImageApply(Image source, Image destination, Rectangle sourceRectangle) + /// The resize options + /// The source image size + public ResizeProcessor(ResizeOptions options, Size sourceSize) { - if (!(this.Sampler is NearestNeighborResampler)) - { - // Since all image frame dimensions have to be the same we can calculate this for all frames. - MemoryAllocator memoryAllocator = source.GetMemoryAllocator(); - this.horizontalKernelMap = ResizeKernelMap.Calculate( - this.Sampler, - this.TargetRectangle.Width, - sourceRectangle.Width, - memoryAllocator); + Guard.NotNull(options, nameof(options)); + Guard.NotNull(options.Sampler, nameof(options.Sampler)); - this.verticalKernelMap = ResizeKernelMap.Calculate( - this.Sampler, - this.TargetRectangle.Height, - sourceRectangle.Height, - memoryAllocator); - } - } + int targetWidth = options.Size.Width; + int targetHeight = options.Size.Height; - /// - protected override void OnFrameApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) - { - // Handle resize dimensions identical to the original - if (source.Width == destination.Width && source.Height == destination.Height && sourceRectangle == this.TargetRectangle) + // Ensure size is populated across both dimensions. + // These dimensions are used to calculate the final dimensions determined by the mode algorithm. + // If only one of the incoming dimensions is 0, it will be modified here to maintain aspect ratio. + // If it is not possible to keep aspect ratio, make sure at least the minimum is is kept. + const int min = 1; + if (targetWidth == 0 && targetHeight > 0) { - // The cloned will be blank here copy all the pixel data over - source.GetPixelSpan().CopyTo(destination.GetPixelSpan()); - return; + targetWidth = (int)MathF.Max(min, MathF.Round(sourceSize.Width * targetHeight / (float)sourceSize.Height)); } - int width = this.Width; - int height = this.Height; - int sourceX = sourceRectangle.X; - int sourceY = sourceRectangle.Y; - int startY = this.TargetRectangle.Y; - int startX = this.TargetRectangle.X; - - var targetWorkingRect = Rectangle.Intersect( - this.TargetRectangle, - new Rectangle(0, 0, width, height)); - - if (this.Sampler is NearestNeighborResampler) + if (targetHeight == 0 && targetWidth > 0) { - // Scaling factors - float widthFactor = sourceRectangle.Width / (float)this.TargetRectangle.Width; - float heightFactor = sourceRectangle.Height / (float)this.TargetRectangle.Height; - - ParallelHelper.IterateRows( - targetWorkingRect, - configuration, - rows => - { - for (int y = rows.Min; y < rows.Max; y++) - { - // Y coordinates of source points - Span sourceRow = - source.GetPixelRowSpan((int)(((y - startY) * heightFactor) + sourceY)); - Span targetRow = destination.GetPixelRowSpan(y); - - for (int x = targetWorkingRect.Left; x < targetWorkingRect.Right; x++) - { - // X coordinates of source points - targetRow[x] = sourceRow[(int)(((x - startX) * widthFactor) + sourceX)]; - } - } - }); - - return; + targetHeight = (int)MathF.Max(min, MathF.Round(sourceSize.Height * targetWidth / (float)sourceSize.Width)); } - int sourceHeight = source.Height; - - PixelConversionModifiers conversionModifiers = - PixelConversionModifiers.Premultiply.ApplyCompanding(this.Compand); - - BufferArea sourceArea = source.PixelBuffer.GetArea(sourceRectangle); + Guard.MustBeGreaterThan(targetWidth, 0, nameof(targetWidth)); + Guard.MustBeGreaterThan(targetHeight, 0, nameof(targetHeight)); - // To reintroduce parallel processing, we to launch multiple workers - // for different row intervals of the image. - using (var worker = new ResizeWorker( - configuration, - sourceArea, - conversionModifiers, - this.horizontalKernelMap, - this.verticalKernelMap, - width, - targetWorkingRect, - this.TargetRectangle.Location)) - { - worker.Initialize(); + (Size size, Rectangle rectangle) = ResizeHelper.CalculateTargetLocationAndBounds(sourceSize, options, targetWidth, targetHeight); - var workingInterval = new RowInterval(targetWorkingRect.Top, targetWorkingRect.Bottom); - worker.FillDestinationPixels(workingInterval, destination.PixelBuffer); - } + this.Sampler = options.Sampler; + this.Width = size.Width; + this.Height = size.Height; + this.TargetRectangle = rectangle; + this.Compand = options.Compand; } - protected override void AfterImageApply(Image source, Image destination, Rectangle sourceRectangle) + /// + /// Initializes a new instance of the class. + /// + /// The sampler to perform the resize operation. + /// The target width. + /// The target height. + /// The source image size + public ResizeProcessor(IResampler sampler, int width, int height, Size sourceSize) + : this(sampler, width, height, sourceSize, new Rectangle(0, 0, width, height), false) { - base.AfterImageApply(source, destination, sourceRectangle); + } - // TODO: An exception in the processing chain can leave these buffers undisposed. We should consider making image processors IDisposable! - this.horizontalKernelMap?.Dispose(); - this.horizontalKernelMap = null; - this.verticalKernelMap?.Dispose(); - this.verticalKernelMap = null; + public IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel + { + return new ResizeProcessorImplementation(this); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessorImplementation.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessorImplementation.cs new file mode 100644 index 0000000000..6c3db3da77 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessorImplementation.cs @@ -0,0 +1,188 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Memory; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Transforms +{ + // The non-generic processor is responsible for: + // - Encapsulating the parameters of the processor + // - Implementing a factory method to create the pixel-specific processor that contains the implementation + + /// + /// Provides methods that allow the resizing of images using various algorithms. + /// Adapted from + /// + /// The pixel format. + internal class ResizeProcessorImplementation : TransformProcessorBase + where TPixel : struct, IPixel + { + // The following fields are not immutable but are optionally created on demand. + private ResizeKernelMap horizontalKernelMap; + private ResizeKernelMap verticalKernelMap; + + private readonly ResizeProcessor parameterSource; + + public ResizeProcessorImplementation(ResizeProcessor parameterSource) + { + this.parameterSource = parameterSource; + } + + /// + /// Gets the sampler to perform the resize operation. + /// + public IResampler Sampler => this.parameterSource.Sampler; + + /// + /// Gets the target width. + /// + public int Width => this.parameterSource.Width; + + /// + /// Gets the target height. + /// + public int Height => this.parameterSource.Height; + + /// + /// Gets the resize rectangle. + /// + public Rectangle TargetRectangle => this.parameterSource.TargetRectangle; + + /// + /// Gets a value indicating whether to compress or expand individual pixel color values on processing. + /// + public bool Compand => this.parameterSource.Compand; + + /// + protected override Image CreateDestination(Image source, Rectangle sourceRectangle) + { + // We will always be creating the clone even for mutate because we may need to resize the canvas + IEnumerable> frames = source.Frames.Select(x => new ImageFrame(source.GetConfiguration(), this.Width, this.Height, x.Metadata.DeepClone())); + + // Use the overload to prevent an extra frame being added + return new Image(source.GetConfiguration(), source.Metadata.DeepClone(), frames); + } + + /// + protected override void BeforeImageApply(Image source, Image destination, Rectangle sourceRectangle) + { + if (!(this.Sampler is NearestNeighborResampler)) + { + // Since all image frame dimensions have to be the same we can calculate this for all frames. + MemoryAllocator memoryAllocator = source.GetMemoryAllocator(); + this.horizontalKernelMap = ResizeKernelMap.Calculate( + this.Sampler, + this.TargetRectangle.Width, + sourceRectangle.Width, + memoryAllocator); + + this.verticalKernelMap = ResizeKernelMap.Calculate( + this.Sampler, + this.TargetRectangle.Height, + sourceRectangle.Height, + memoryAllocator); + } + } + + /// + protected override void OnFrameApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) + { + // Handle resize dimensions identical to the original + if (source.Width == destination.Width && source.Height == destination.Height && sourceRectangle == this.TargetRectangle) + { + // The cloned will be blank here copy all the pixel data over + source.GetPixelSpan().CopyTo(destination.GetPixelSpan()); + return; + } + + int width = this.Width; + int height = this.Height; + int sourceX = sourceRectangle.X; + int sourceY = sourceRectangle.Y; + int startY = this.TargetRectangle.Y; + int startX = this.TargetRectangle.X; + + var targetWorkingRect = Rectangle.Intersect( + this.TargetRectangle, + new Rectangle(0, 0, width, height)); + + if (this.Sampler is NearestNeighborResampler) + { + // Scaling factors + float widthFactor = sourceRectangle.Width / (float)this.TargetRectangle.Width; + float heightFactor = sourceRectangle.Height / (float)this.TargetRectangle.Height; + + ParallelHelper.IterateRows( + targetWorkingRect, + configuration, + rows => + { + for (int y = rows.Min; y < rows.Max; y++) + { + // Y coordinates of source points + Span sourceRow = + source.GetPixelRowSpan((int)(((y - startY) * heightFactor) + sourceY)); + Span targetRow = destination.GetPixelRowSpan(y); + + for (int x = targetWorkingRect.Left; x < targetWorkingRect.Right; x++) + { + // X coordinates of source points + targetRow[x] = sourceRow[(int)(((x - startX) * widthFactor) + sourceX)]; + } + } + }); + + return; + } + + int sourceHeight = source.Height; + + PixelConversionModifiers conversionModifiers = + PixelConversionModifiers.Premultiply.ApplyCompanding(this.Compand); + + BufferArea sourceArea = source.PixelBuffer.GetArea(sourceRectangle); + + // To reintroduce parallel processing, we to launch multiple workers + // for different row intervals of the image. + using (var worker = new ResizeWorker( + configuration, + sourceArea, + conversionModifiers, + this.horizontalKernelMap, + this.verticalKernelMap, + width, + targetWorkingRect, + this.TargetRectangle.Location)) + { + worker.Initialize(); + + var workingInterval = new RowInterval(targetWorkingRect.Top, targetWorkingRect.Bottom); + worker.FillDestinationPixels(workingInterval, destination.PixelBuffer); + } + } + + protected override void AfterImageApply(Image source, Image destination, Rectangle sourceRectangle) + { + base.AfterImageApply(source, destination, sourceRectangle); + + // TODO: An exception in the processing chain can leave these buffers undisposed. We should consider making image processors IDisposable! + this.horizontalKernelMap?.Dispose(); + this.horizontalKernelMap = null; + this.verticalKernelMap?.Dispose(); + this.verticalKernelMap = null; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/ResizeExtensions.cs b/src/ImageSharp/Processing/ResizeExtensions.cs index 7b6c14d7de..cf029eb152 100644 --- a/src/ImageSharp/Processing/ResizeExtensions.cs +++ b/src/ImageSharp/Processing/ResizeExtensions.cs @@ -20,9 +20,8 @@ namespace SixLabors.ImageSharp.Processing /// The resize options. /// The /// Passing zero for one of height or width within the resize options will automatically preserve the aspect ratio of the original image or the nearest possible ratio. - public static IImageProcessingContext Resize(this IImageProcessingContext source, ResizeOptions options) - where TPixel : struct, IPixel - => source.ApplyProcessor(new ResizeProcessor(options, source.GetCurrentSize())); + public static IImageProcessingContext Resize(this IImageProcessingContext source, ResizeOptions options) + => source.ApplyProcessor(new ResizeProcessor(options, source.GetCurrentSize())); /// /// Resizes an image to the given . @@ -32,8 +31,7 @@ namespace SixLabors.ImageSharp.Processing /// The target image size. /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. - public static IImageProcessingContext Resize(this IImageProcessingContext source, Size size) - where TPixel : struct, IPixel + public static IImageProcessingContext Resize(this IImageProcessingContext source, Size size) => Resize(source, size.Width, size.Height, KnownResamplers.Bicubic, false); /// @@ -45,8 +43,7 @@ namespace SixLabors.ImageSharp.Processing /// Whether to compress and expand the image color-space to gamma correct the image during processing. /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. - public static IImageProcessingContext Resize(this IImageProcessingContext source, Size size, bool compand) - where TPixel : struct, IPixel + public static IImageProcessingContext Resize(this IImageProcessingContext source, Size size, bool compand) => Resize(source, size.Width, size.Height, KnownResamplers.Bicubic, compand); /// @@ -58,8 +55,7 @@ namespace SixLabors.ImageSharp.Processing /// The target image height. /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. - public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height) - where TPixel : struct, IPixel + public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height) => Resize(source, width, height, KnownResamplers.Bicubic, false); /// @@ -72,8 +68,7 @@ namespace SixLabors.ImageSharp.Processing /// Whether to compress and expand the image color-space to gamma correct the image during processing. /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. - public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, bool compand) - where TPixel : struct, IPixel + public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, bool compand) => Resize(source, width, height, KnownResamplers.Bicubic, compand); /// @@ -86,8 +81,7 @@ namespace SixLabors.ImageSharp.Processing /// The to perform the resampling. /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. - public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, IResampler sampler) - where TPixel : struct, IPixel + public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, IResampler sampler) => Resize(source, width, height, sampler, false); /// @@ -100,8 +94,7 @@ namespace SixLabors.ImageSharp.Processing /// Whether to compress and expand the image color-space to gamma correct the image during processing. /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. - public static IImageProcessingContext Resize(this IImageProcessingContext source, Size size, IResampler sampler, bool compand) - where TPixel : struct, IPixel + public static IImageProcessingContext Resize(this IImageProcessingContext source, Size size, IResampler sampler, bool compand) => Resize(source, size.Width, size.Height, sampler, new Rectangle(0, 0, size.Width, size.Height), compand); /// @@ -115,8 +108,7 @@ namespace SixLabors.ImageSharp.Processing /// Whether to compress and expand the image color-space to gamma correct the image during processing. /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. - public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, IResampler sampler, bool compand) - where TPixel : struct, IPixel + public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, IResampler sampler, bool compand) => Resize(source, width, height, sampler, new Rectangle(0, 0, width, height), compand); /// @@ -137,16 +129,15 @@ namespace SixLabors.ImageSharp.Processing /// Whether to compress and expand the image color-space to gamma correct the image during processing. /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. - public static IImageProcessingContext Resize( - this IImageProcessingContext source, + public static IImageProcessingContext Resize( + this IImageProcessingContext source, int width, int height, IResampler sampler, Rectangle sourceRectangle, Rectangle targetRectangle, bool compand) - where TPixel : struct, IPixel - => source.ApplyProcessor(new ResizeProcessor(sampler, width, height, source.GetCurrentSize(), targetRectangle, compand), sourceRectangle); + => source.ApplyProcessor(new ResizeProcessor(sampler, width, height, source.GetCurrentSize(), targetRectangle, compand), sourceRectangle); /// /// Resizes an image to the given width and height with the given sampler and source rectangle. @@ -162,14 +153,13 @@ namespace SixLabors.ImageSharp.Processing /// Whether to compress and expand the image color-space to gamma correct the image during processing. /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. - public static IImageProcessingContext Resize( - this IImageProcessingContext source, + public static IImageProcessingContext Resize( + this IImageProcessingContext source, int width, int height, IResampler sampler, Rectangle targetRectangle, bool compand) - where TPixel : struct, IPixel - => source.ApplyProcessor(new ResizeProcessor(sampler, width, height, source.GetCurrentSize(), targetRectangle, compand)); + => source.ApplyProcessor(new ResizeProcessor(sampler, width, height, source.GetCurrentSize(), targetRectangle, compand)); } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs b/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs index ff4014e616..276cc5da85 100644 --- a/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs +++ b/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs @@ -67,6 +67,16 @@ namespace SixLabors.ImageSharp.Tests return this.Source.Size(); } + public IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle) + { + throw new System.NotImplementedException(); + } + + public IImageProcessingContext ApplyProcessor(IImageProcessor processor) + { + throw new System.NotImplementedException(); + } + public IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle) { this.Applied.Add(new AppliedOperation diff --git a/tests/ImageSharp.Tests/Image/ImageProcessingContextTests.cs b/tests/ImageSharp.Tests/Image/ImageProcessingContextTests.cs index 041b6c8468..6dadc6e7a2 100644 --- a/tests/ImageSharp.Tests/Image/ImageProcessingContextTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageProcessingContextTests.cs @@ -10,43 +10,43 @@ namespace SixLabors.ImageSharp.Tests { public class ImageProcessingContextTests { - [Fact] - public void MutatedSizeIsAccuratePerOperation() - { - var x500 = new Size(500, 500); - var x400 = new Size(400, 400); - var x300 = new Size(300, 300); - var x200 = new Size(200, 200); - var x100 = new Size(100, 100); - using (var image = new Image(500, 500)) - { - image.Mutate(x => - x.AssertSize(x500) - .Resize(x400).AssertSize(x400) - .Resize(x300).AssertSize(x300) - .Resize(x200).AssertSize(x200) - .Resize(x100).AssertSize(x100)); - } - } - - [Fact] - public void ClonedSizeIsAccuratePerOperation() - { - var x500 = new Size(500, 500); - var x400 = new Size(400, 400); - var x300 = new Size(300, 300); - var x200 = new Size(200, 200); - var x100 = new Size(100, 100); - using (var image = new Image(500, 500)) - { - image.Clone(x => - x.AssertSize(x500) - .Resize(x400).AssertSize(x400) - .Resize(x300).AssertSize(x300) - .Resize(x200).AssertSize(x200) - .Resize(x100).AssertSize(x100)); - } - } + // [Fact] + // public void MutatedSizeIsAccuratePerOperation() + // { + // var x500 = new Size(500, 500); + // var x400 = new Size(400, 400); + // var x300 = new Size(300, 300); + // var x200 = new Size(200, 200); + // var x100 = new Size(100, 100); + // using (var image = new Image(500, 500)) + // { + // image.Mutate(x => + // x.AssertSize(x500) + // .Resize(x400).AssertSize(x400) + // .Resize(x300).AssertSize(x300) + // .Resize(x200).AssertSize(x200) + // .Resize(x100).AssertSize(x100)); + // } + // } + // + // [Fact] + // public void ClonedSizeIsAccuratePerOperation() + // { + // var x500 = new Size(500, 500); + // var x400 = new Size(400, 400); + // var x300 = new Size(300, 300); + // var x200 = new Size(200, 200); + // var x100 = new Size(100, 100); + // using (var image = new Image(500, 500)) + // { + // image.Clone(x => + // x.AssertSize(x500) + // .Resize(x400).AssertSize(x400) + // .Resize(x300).AssertSize(x300) + // .Resize(x200).AssertSize(x200) + // .Resize(x100).AssertSize(x100)); + // } + // } } public static class SizeAssertationExtensions From ebfecf4fda287502d83dd2d21ec9e6d1653e8b31 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Wed, 1 May 2019 22:25:37 +0200 Subject: [PATCH 103/223] pixel-agnostic decoder API --- .../Advanced/AdvancedImageExtensions.cs | 3 +- src/ImageSharp/Formats/Bmp/BmpDecoder.cs | 2 + src/ImageSharp/Formats/Gif/GifDecoder.cs | 2 + src/ImageSharp/Formats/IImageDecoder.cs | 2 + src/ImageSharp/Formats/Jpeg/JpegDecoder.cs | 2 + src/ImageSharp/Formats/Png/PngDecoder.cs | 2 + src/ImageSharp/Image.Decode.cs | 12 ++++++ src/ImageSharp/Image.FromFile.cs | 30 ++++++-------- src/ImageSharp/Image.FromStream.cs | 41 ++++++++++++------- src/ImageSharp/Image.cs | 36 ++++++++++++++++ src/ImageSharp/ImageExtensions.cs | 9 ++-- src/ImageSharp/Image{TPixel}.cs | 14 ------- .../Processors/Transforms/ResizeTests.cs | 16 ++++++++ tests/ImageSharp.Tests/TestFormat.cs | 2 + .../ReferenceCodecs/MagickReferenceDecoder.cs | 2 + .../SystemDrawingReferenceDecoder.cs | 2 + .../Tests/TestImageProviderTests.cs | 4 ++ 17 files changed, 127 insertions(+), 54 deletions(-) diff --git a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs index bdcb4c10f1..378b6e8b96 100644 --- a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs +++ b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs @@ -21,8 +21,7 @@ namespace SixLabors.ImageSharp.Advanced /// The Pixel format. /// The source image. /// Returns the configuration. - public static Configuration GetConfiguration(this Image source) - where TPixel : struct, IPixel + public static Configuration GetConfiguration(this Image source) => GetConfiguration((IConfigurable)source); /// diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs index 82af2a671e..4922a92003 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs @@ -30,6 +30,8 @@ namespace SixLabors.ImageSharp.Formats.Bmp return new BmpDecoderCore(configuration, this).Decode(stream); } + public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); + /// public IImageInfo Identify(Configuration configuration, Stream stream) { diff --git a/src/ImageSharp/Formats/Gif/GifDecoder.cs b/src/ImageSharp/Formats/Gif/GifDecoder.cs index 6af75f2d0f..6498f4aa27 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoder.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoder.cs @@ -44,5 +44,7 @@ namespace SixLabors.ImageSharp.Formats.Gif var decoder = new GifDecoderCore(configuration, this); return decoder.Identify(stream); } + + public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); } } diff --git a/src/ImageSharp/Formats/IImageDecoder.cs b/src/ImageSharp/Formats/IImageDecoder.cs index ffc40314d8..625c4efb6f 100644 --- a/src/ImageSharp/Formats/IImageDecoder.cs +++ b/src/ImageSharp/Formats/IImageDecoder.cs @@ -20,5 +20,7 @@ namespace SixLabors.ImageSharp.Formats /// The decoded image Image Decode(Configuration configuration, Stream stream) where TPixel : struct, IPixel; + + Image Decode(Configuration configuration, Stream stream); } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs index 57b70dd26e..7459abec56 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs @@ -38,5 +38,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg return decoder.Identify(stream); } } + + public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Png/PngDecoder.cs b/src/ImageSharp/Formats/Png/PngDecoder.cs index 39dfb1d0bd..e8c5ac8e81 100644 --- a/src/ImageSharp/Formats/Png/PngDecoder.cs +++ b/src/ImageSharp/Formats/Png/PngDecoder.cs @@ -59,5 +59,7 @@ namespace SixLabors.ImageSharp.Formats.Png var decoder = new PngDecoderCore(configuration, this); return decoder.Identify(stream); } + + public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); } } \ No newline at end of file diff --git a/src/ImageSharp/Image.Decode.cs b/src/ImageSharp/Image.Decode.cs index 9e83d173f2..36944dc2c0 100644 --- a/src/ImageSharp/Image.Decode.cs +++ b/src/ImageSharp/Image.Decode.cs @@ -102,6 +102,18 @@ namespace SixLabors.ImageSharp Image img = decoder.Decode(config, stream); return (img, format); } + + private static (Image img, IImageFormat format) Decode(Stream stream, Configuration config) + { + IImageDecoder decoder = DiscoverDecoder(stream, config, out IImageFormat format); + if (decoder is null) + { + return (null, null); + } + + Image img = decoder.Decode(config, stream); + return (img, format); + } /// /// Reads the raw image information from the specified stream. diff --git a/src/ImageSharp/Image.FromFile.cs b/src/ImageSharp/Image.FromFile.cs index b13cef4824..80267146d5 100644 --- a/src/ImageSharp/Image.FromFile.cs +++ b/src/ImageSharp/Image.FromFile.cs @@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp /// Thrown if the stream is not readable nor seekable. /// /// A new . - public static Image Load(string path) => Load(path); + public static Image Load(string path) => Load(path); /// /// Create a new instance of the class from the given file. @@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp /// Thrown if the stream is not readable nor seekable. /// /// A new . - public static Image Load(string path, out IImageFormat format) => Load(path, out format); + public static Image Load(string path, out IImageFormat format) => Load(path, out format); /// /// Create a new instance of the class from the given file. @@ -68,19 +68,7 @@ namespace SixLabors.ImageSharp /// Thrown if the stream is not readable nor seekable. /// /// A new . - public static Image Load(Configuration config, string path) => Load(config, path); - - /// - /// Create a new instance of the class from the given file. - /// - /// The config for the decoder. - /// The file path to the image. - /// The mime type of the decoded image. - /// - /// Thrown if the stream is not readable nor seekable. - /// - /// A new . - public static Image Load(Configuration config, string path, out IImageFormat format) => Load(config, path, out format); + public static Image Load(Configuration config, string path) => Load(config, path); /// /// Create a new instance of the class from the given file. @@ -92,7 +80,7 @@ namespace SixLabors.ImageSharp /// Thrown if the stream is not readable nor seekable. /// /// A new . - public static Image Load(Configuration config, string path, IImageDecoder decoder) => Load(config, path, decoder); + public static Image Load(Configuration config, string path, IImageDecoder decoder) => Load(config, path, decoder); /// /// Create a new instance of the class from the given file. @@ -103,7 +91,7 @@ namespace SixLabors.ImageSharp /// Thrown if the stream is not readable nor seekable. /// /// A new . - public static Image Load(string path, IImageDecoder decoder) => Load(path, decoder); + public static Image Load(string path, IImageDecoder decoder) => Load(path, decoder); /// /// Create a new instance of the class from the given file. @@ -174,6 +162,14 @@ namespace SixLabors.ImageSharp return Load(config, stream, out format); } } + + public static Image Load(Configuration config, string path, out IImageFormat format) + { + using (Stream stream = config.FileSystem.OpenRead(path)) + { + return Load(config, stream, out format); + } + } /// /// Create a new instance of the class from the given file. diff --git a/src/ImageSharp/Image.FromStream.cs b/src/ImageSharp/Image.FromStream.cs index 3236e00072..8848225f54 100644 --- a/src/ImageSharp/Image.FromStream.cs +++ b/src/ImageSharp/Image.FromStream.cs @@ -62,7 +62,7 @@ namespace SixLabors.ImageSharp /// the mime type of the decoded image. /// Thrown if the stream is not readable. /// A new .> - public static Image Load(Stream stream, out IImageFormat format) => Load(stream, out format); + public static Image Load(Stream stream, out IImageFormat format) => Load(stream, out format); /// /// Create a new instance of the class from the given stream. @@ -70,7 +70,7 @@ namespace SixLabors.ImageSharp /// The stream containing image information. /// Thrown if the stream is not readable. /// A new .> - public static Image Load(Stream stream) => Load(stream); + public static Image Load(Stream stream) => Load(stream); /// /// Create a new instance of the class from the given stream. @@ -79,7 +79,7 @@ namespace SixLabors.ImageSharp /// The decoder. /// Thrown if the stream is not readable. /// A new .> - public static Image Load(Stream stream, IImageDecoder decoder) => Load(stream, decoder); + public static Image Load(Stream stream, IImageDecoder decoder) => Load(stream, decoder); /// /// Create a new instance of the class from the given stream. @@ -88,18 +88,7 @@ namespace SixLabors.ImageSharp /// The stream containing image information. /// Thrown if the stream is not readable. /// A new .> - public static Image Load(Configuration config, Stream stream) => Load(config, stream); - - /// - /// Create a new instance of the class from the given stream. - /// - /// The config for the decoder. - /// The stream containing image information. - /// the mime type of the decoded image. - /// Thrown if the stream is not readable. - /// A new .> - public static Image Load(Configuration config, Stream stream, out IImageFormat format) - => Load(config, stream, out format); + public static Image Load(Configuration config, Stream stream) => Load(config, stream); /// /// Create a new instance of the class from the given stream. @@ -193,6 +182,28 @@ namespace SixLabors.ImageSharp throw new NotSupportedException(sb.ToString()); } + private static Image Load(Configuration config, Stream stream, out IImageFormat format) + { + config = config ?? Configuration.Default; + (Image img, IImageFormat format) data = WithSeekableStream(config, stream, s => Decode(s, config)); + + format = data.format; + + if (data.img != null) + { + return data.img; + } + + var sb = new StringBuilder(); + sb.AppendLine("Image cannot be loaded. Available decoders:"); + + foreach (KeyValuePair val in config.ImageFormatsManager.ImageDecoders) + { + sb.AppendLine($" - {val.Key.Name} : {val.Value.GetType().Name}"); + } + + throw new NotSupportedException(sb.ToString()); + } private static T WithSeekableStream(Configuration config, Stream stream, Func action) { diff --git a/src/ImageSharp/Image.cs b/src/ImageSharp/Image.cs index 00a9a20755..a27f16ab30 100644 --- a/src/ImageSharp/Image.cs +++ b/src/ImageSharp/Image.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System.IO; + using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Metadata; @@ -45,5 +47,39 @@ namespace SixLabors.ImageSharp public abstract void Dispose(); internal abstract void AcceptVisitor(IImageVisitor visitor); + + /// + /// Saves the image to the given stream using the given image encoder. + /// + /// The stream to save the image to. + /// The encoder to save the image with. + /// Thrown if the stream or encoder is null. + public void Save(Stream stream, IImageEncoder encoder) + { + Guard.NotNull(stream, nameof(stream)); + Guard.NotNull(encoder, nameof(encoder)); + + EncodeVisitor visitor = new EncodeVisitor(encoder, stream); + this.AcceptVisitor(visitor); + } + + class EncodeVisitor : IImageVisitor + { + private readonly IImageEncoder encoder; + + private readonly Stream stream; + + public EncodeVisitor(IImageEncoder encoder, Stream stream) + { + this.encoder = encoder; + this.stream = stream; + } + + public void Visit(Image image) + where TPixel : struct, IPixel + { + this.encoder.Encode(image, this.stream); + } + } } } \ No newline at end of file diff --git a/src/ImageSharp/ImageExtensions.cs b/src/ImageSharp/ImageExtensions.cs index 5010451b8e..3bd60694d5 100644 --- a/src/ImageSharp/ImageExtensions.cs +++ b/src/ImageSharp/ImageExtensions.cs @@ -23,8 +23,7 @@ namespace SixLabors.ImageSharp /// The source image. /// The file path to save the image to. /// Thrown if the stream is null. - public static void Save(this Image source, string filePath) - where TPixel : struct, IPixel + public static void Save(this Image source, string filePath) { Guard.NotNullOrWhiteSpace(filePath, nameof(filePath)); @@ -67,8 +66,7 @@ namespace SixLabors.ImageSharp /// The file path to save the image to. /// The encoder to save the image with. /// Thrown if the encoder is null. - public static void Save(this Image source, string filePath, IImageEncoder encoder) - where TPixel : struct, IPixel + public static void Save(this Image source, string filePath, IImageEncoder encoder) { Guard.NotNull(encoder, nameof(encoder)); using (Stream fs = source.GetConfiguration().FileSystem.Create(filePath)) @@ -85,8 +83,7 @@ namespace SixLabors.ImageSharp /// The stream to save the image to. /// The format to save the image in. /// Thrown if the stream is null. - public static void Save(this Image source, Stream stream, IImageFormat format) - where TPixel : struct, IPixel + public static void Save(this Image source, Stream stream, IImageFormat format) { Guard.NotNull(format, nameof(format)); IImageEncoder encoder = source.GetConfiguration().ImageFormatsManager.FindEncoder(format); diff --git a/src/ImageSharp/Image{TPixel}.cs b/src/ImageSharp/Image{TPixel}.cs index 64ec87c4dd..e37f3e4c1c 100644 --- a/src/ImageSharp/Image{TPixel}.cs +++ b/src/ImageSharp/Image{TPixel}.cs @@ -158,20 +158,6 @@ namespace SixLabors.ImageSharp set => this.PixelSource.PixelBuffer[x, y] = value; } - /// - /// Saves the image to the given stream using the given image encoder. - /// - /// The stream to save the image to. - /// The encoder to save the image with. - /// Thrown if the stream or encoder is null. - public void Save(Stream stream, IImageEncoder encoder) - { - Guard.NotNull(stream, nameof(stream)); - Guard.NotNull(encoder, nameof(encoder)); - - encoder.Encode(this, stream); - } - /// /// Clones the current image /// diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs index e3a43a652e..c7af8d4688 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs @@ -42,6 +42,22 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.07F); + [Fact] + public void Resize_PixelAgnostic() + { + var filePath = TestFile.GetInputFileFullPath(TestImages.Jpeg.Baseline.Calliphora); + + using (Image image = Image.Load(filePath)) + { + image.Mutate(x => x.Resize(image.Size() / 2)); + string path = System.IO.Path.Combine( + TestEnvironment.CreateOutputDirectory(nameof(ResizeTests)), + nameof(this.Resize_PixelAgnostic) + ".png"); + + image.Save(path); + } + } + [Theory( Skip = "Debug only, enable manually" )] diff --git a/tests/ImageSharp.Tests/TestFormat.cs b/tests/ImageSharp.Tests/TestFormat.cs index 64357a17e1..91e31b356e 100644 --- a/tests/ImageSharp.Tests/TestFormat.cs +++ b/tests/ImageSharp.Tests/TestFormat.cs @@ -199,6 +199,8 @@ namespace SixLabors.ImageSharp.Tests } public bool IsSupportedFileFormat(Span header) => testFormat.IsSupportedFileFormat(header); + + public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); } public class TestEncoder : ImageSharp.Formats.IImageEncoder diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs index 3dd330e4d3..e81714ddc4 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs @@ -57,5 +57,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs return result; } } + + public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs index 7a775c0817..2de3c03aaf 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs @@ -51,5 +51,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs return new ImageInfo(pixelType, sourceBitmap.Width, sourceBitmap.Height, new ImageMetadata()); } } + + public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs index 4ef6a582c9..b49baa5c41 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs @@ -379,6 +379,8 @@ namespace SixLabors.ImageSharp.Tests this.callerName = name; invocationCounts[name] = 0; } + + public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); } private class TestDecoderWithParameters : IImageDecoder @@ -416,6 +418,8 @@ namespace SixLabors.ImageSharp.Tests this.callerName = name; invocationCounts[name] = 0; } + + public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); } } } \ No newline at end of file From ff045a13624575d38dd3cf142c074511c8aeb1b1 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Wed, 1 May 2019 22:38:25 +0200 Subject: [PATCH 104/223] refactor FilterProcessor stuff --- src/ImageSharp/IImage.cs | 2 +- .../Processing/BlackWhiteExtensions.cs | 12 ++-- src/ImageSharp/Processing/FilterExtensions.cs | 12 ++-- .../Processing/OpacityExtensions.cs | 12 ++-- .../Processors/Filters/BlackWhiteProcessor.cs | 3 +- .../Processors/Filters/FilterProcessor.cs | 43 ++------------- .../Filters/FilterProcessorImplementation.cs | 55 +++++++++++++++++++ .../Processors/Filters/OpacityProcessor.cs | 3 +- .../Transforms/Resize/ResizeProcessor.cs | 7 ++- .../Resize/ResizeProcessorImplementation.cs | 4 -- .../Processors/Transforms/ResizeTests.cs | 1 + 11 files changed, 82 insertions(+), 72 deletions(-) create mode 100644 src/ImageSharp/Processing/Processors/Filters/FilterProcessorImplementation.cs diff --git a/src/ImageSharp/IImage.cs b/src/ImageSharp/IImage.cs index fb251cfd5a..b9e2cee616 100644 --- a/src/ImageSharp/IImage.cs +++ b/src/ImageSharp/IImage.cs @@ -4,7 +4,7 @@ using System; namespace SixLabors.ImageSharp -{ +{ /// /// Encapsulates the properties and methods that describe an image. /// diff --git a/src/ImageSharp/Processing/BlackWhiteExtensions.cs b/src/ImageSharp/Processing/BlackWhiteExtensions.cs index 0484fa84e1..5dc2341e57 100644 --- a/src/ImageSharp/Processing/BlackWhiteExtensions.cs +++ b/src/ImageSharp/Processing/BlackWhiteExtensions.cs @@ -15,24 +15,20 @@ namespace SixLabors.ImageSharp.Processing /// /// Applies black and white toning to the image. /// - /// The pixel format. /// The image this method extends. /// The . - public static IImageProcessingContext BlackWhite(this IImageProcessingContext source) - where TPixel : struct, IPixel - => source.ApplyProcessor(new BlackWhiteProcessor()); + public static IImageProcessingContext BlackWhite(this IImageProcessingContext source) + => source.ApplyProcessor(new BlackWhiteProcessor()); /// /// Applies black and white toning to the image. /// - /// The pixel format. /// The image this method extends. /// /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext BlackWhite(this IImageProcessingContext source, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new BlackWhiteProcessor(), rectangle); + public static IImageProcessingContext BlackWhite(this IImageProcessingContext source, Rectangle rectangle) + => source.ApplyProcessor(new BlackWhiteProcessor(), rectangle); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/FilterExtensions.cs b/src/ImageSharp/Processing/FilterExtensions.cs index 70ac232863..2a1c6bc7f9 100644 --- a/src/ImageSharp/Processing/FilterExtensions.cs +++ b/src/ImageSharp/Processing/FilterExtensions.cs @@ -16,26 +16,22 @@ namespace SixLabors.ImageSharp.Processing /// /// Filters an image but the given color matrix /// - /// The pixel format. /// The image this method extends. /// The filter color matrix /// The . - public static IImageProcessingContext Filter(this IImageProcessingContext source, ColorMatrix matrix) - where TPixel : struct, IPixel - => source.ApplyProcessor(new FilterProcessor(matrix)); + public static IImageProcessingContext Filter(this IImageProcessingContext source, ColorMatrix matrix) + => source.ApplyProcessor(new FilterProcessor(matrix)); /// /// Filters an image but the given color matrix /// - /// The pixel format. /// The image this method extends. /// The filter color matrix /// /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext Filter(this IImageProcessingContext source, ColorMatrix matrix, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new FilterProcessor(matrix), rectangle); + public static IImageProcessingContext Filter(this IImageProcessingContext source, ColorMatrix matrix, Rectangle rectangle) + => source.ApplyProcessor(new FilterProcessor(matrix), rectangle); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/OpacityExtensions.cs b/src/ImageSharp/Processing/OpacityExtensions.cs index fc3fd331de..6d9198648c 100644 --- a/src/ImageSharp/Processing/OpacityExtensions.cs +++ b/src/ImageSharp/Processing/OpacityExtensions.cs @@ -15,26 +15,22 @@ namespace SixLabors.ImageSharp.Processing /// /// Multiplies the alpha component of the image. /// - /// The pixel format. /// The image this method extends. /// The proportion of the conversion. Must be between 0 and 1. /// The . - public static IImageProcessingContext Opacity(this IImageProcessingContext source, float amount) - where TPixel : struct, IPixel - => source.ApplyProcessor(new OpacityProcessor(amount)); + public static IImageProcessingContext Opacity(this IImageProcessingContext source, float amount) + => source.ApplyProcessor(new OpacityProcessor(amount)); /// /// Multiplies the alpha component of the image. /// - /// The pixel format. /// The image this method extends. /// The proportion of the conversion. Must be between 0 and 1. /// /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext Opacity(this IImageProcessingContext source, float amount, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new OpacityProcessor(amount), rectangle); + public static IImageProcessingContext Opacity(this IImageProcessingContext source, float amount, Rectangle rectangle) + => source.ApplyProcessor(new OpacityProcessor(amount), rectangle); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/BlackWhiteProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/BlackWhiteProcessor.cs index 9925ce5c21..8bf925cf0f 100644 --- a/src/ImageSharp/Processing/Processors/Filters/BlackWhiteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/BlackWhiteProcessor.cs @@ -9,8 +9,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// Applies a black and white filter matrix to the image /// /// The pixel format. - internal class BlackWhiteProcessor : FilterProcessor - where TPixel : struct, IPixel + internal class BlackWhiteProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs index dbd8433410..3f9f586877 100644 --- a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs @@ -1,25 +1,15 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; -using System.Numerics; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Filters { - /// - /// Provides methods that accept a matrix to apply free-form filters to images. - /// - /// The pixel format. - internal class FilterProcessor : ImageProcessor - where TPixel : struct, IPixel + public class FilterProcessor : IImageProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The matrix used to apply the image filter public FilterProcessor(ColorMatrix matrix) => this.Matrix = matrix; @@ -29,31 +19,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// public ColorMatrix Matrix { get; } - /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + public IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel { - var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); - int startX = interest.X; - - ColorMatrix matrix = this.Matrix; - - ParallelHelper.IterateRowsWithTempBuffer( - interest, - configuration, - (rows, vectorBuffer) => - { - for (int y = rows.Min; y < rows.Max; y++) - { - Span vectorSpan = vectorBuffer.Span; - int length = vectorSpan.Length; - Span rowSpan = source.GetPixelRowSpan(y).Slice(startX, length); - PixelOperations.Instance.ToVector4(configuration, rowSpan, vectorSpan); - - Vector4Utils.Transform(vectorSpan, ref matrix); - - PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan, rowSpan); - } - }); + return new FilterProcessorImplementation(this); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/FilterProcessorImplementation.cs b/src/ImageSharp/Processing/Processors/Filters/FilterProcessorImplementation.cs new file mode 100644 index 0000000000..1eaaf1ea5b --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Filters/FilterProcessorImplementation.cs @@ -0,0 +1,55 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Primitives; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Filters +{ + /// + /// Provides methods that accept a matrix to apply free-form filters to images. + /// + /// The pixel format. + internal class FilterProcessorImplementation : ImageProcessor + where TPixel : struct, IPixel + { + private readonly FilterProcessor paramterSource; + + public FilterProcessorImplementation(FilterProcessor paramterSource) + { + this.paramterSource = paramterSource; + } + + /// + protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); + int startX = interest.X; + + ColorMatrix matrix = this.paramterSource.Matrix; + + ParallelHelper.IterateRowsWithTempBuffer( + interest, + configuration, + (rows, vectorBuffer) => + { + for (int y = rows.Min; y < rows.Max; y++) + { + Span vectorSpan = vectorBuffer.Span; + int length = vectorSpan.Length; + Span rowSpan = source.GetPixelRowSpan(y).Slice(startX, length); + PixelOperations.Instance.ToVector4(configuration, rowSpan, vectorSpan); + + Vector4Utils.Transform(vectorSpan, ref matrix); + + PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan, rowSpan); + } + }); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs index 0fea61cad9..2d6bf6e833 100644 --- a/src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs @@ -9,8 +9,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// Applies an opacity filter matrix using the given amount. /// /// The pixel format. - internal class OpacityProcessor : FilterProcessor - where TPixel : struct, IPixel + internal class OpacityProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs index 4348ecdaba..d3077b7e65 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs @@ -1,5 +1,5 @@ -// // Copyright (c) Six Labors and contributors. -// // Licensed under the Apache License, Version 2.0. +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. using System; @@ -8,6 +8,9 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { + // The non-generic processor is responsible for: + // - Encapsulating the parameters of the processor + // - Implementing a factory method to create the pixel-specific processor that contains the implementation public class ResizeProcessor : IImageProcessor { /// diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessorImplementation.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessorImplementation.cs index 6c3db3da77..1a8bb931e0 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessorImplementation.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessorImplementation.cs @@ -18,10 +18,6 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { - // The non-generic processor is responsible for: - // - Encapsulating the parameters of the processor - // - Implementing a factory method to create the pixel-specific processor that contains the implementation - /// /// Provides methods that allow the resizing of images using various algorithms. /// Adapted from diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs index c7af8d4688..9bf9079d41 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs @@ -40,6 +40,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms nameof(KnownResamplers.Lanczos5), }; + private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.07F); [Fact] From 1978731ac12de8f5b745763a33e0e5b347257124 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 4 May 2019 00:15:12 +0200 Subject: [PATCH 105/223] basic fixes after rebase + temporarily comment out target frameworks --- src/ImageSharp/Formats/PixelTypeInfo.cs | 8 ++++++++ src/ImageSharp/Image.Decode.cs | 2 +- src/ImageSharp/Image.FromBytes.cs | 2 +- src/ImageSharp/Image.FromFile.cs | 2 +- src/ImageSharp/Image.FromStream.cs | 2 +- src/ImageSharp/Image.LoadPixelData.cs | 2 +- src/ImageSharp/Image.WrapMemory.cs | 2 +- src/ImageSharp/ImageSharp.csproj | 5 ++++- src/ImageSharp/Image{TPixel}.cs | 6 +++--- 9 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/ImageSharp/Formats/PixelTypeInfo.cs b/src/ImageSharp/Formats/PixelTypeInfo.cs index ed21b91bfc..66d04f39fd 100644 --- a/src/ImageSharp/Formats/PixelTypeInfo.cs +++ b/src/ImageSharp/Formats/PixelTypeInfo.cs @@ -1,6 +1,10 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System.Runtime.CompilerServices; + +using SixLabors.ImageSharp.PixelFormats; + namespace SixLabors.ImageSharp.Formats { /// @@ -21,5 +25,9 @@ namespace SixLabors.ImageSharp.Formats /// Gets color depth, in number of bits per pixel. /// public int BitsPerPixel { get; } + + internal static PixelTypeInfo Create() + where TPixel : struct, IPixel => + new PixelTypeInfo(Unsafe.SizeOf() * 8); } } diff --git a/src/ImageSharp/Image.Decode.cs b/src/ImageSharp/Image.Decode.cs index 36944dc2c0..17a15a9160 100644 --- a/src/ImageSharp/Image.Decode.cs +++ b/src/ImageSharp/Image.Decode.cs @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp /// /// Adds static methods allowing the decoding of new images. /// - public static partial class Image + public abstract partial class Image { /// /// Creates an instance backed by an uninitialized memory buffer. diff --git a/src/ImageSharp/Image.FromBytes.cs b/src/ImageSharp/Image.FromBytes.cs index 7ceeea9498..24a169f6e9 100644 --- a/src/ImageSharp/Image.FromBytes.cs +++ b/src/ImageSharp/Image.FromBytes.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp /// /// Adds static methods allowing the creation of new image from a byte array. /// - public static partial class Image + public abstract partial class Image { /// /// By reading the header on the provided byte array this calculates the images format. diff --git a/src/ImageSharp/Image.FromFile.cs b/src/ImageSharp/Image.FromFile.cs index 80267146d5..0b49668862 100644 --- a/src/ImageSharp/Image.FromFile.cs +++ b/src/ImageSharp/Image.FromFile.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp /// /// Adds static methods allowing the creation of new image from a given file. /// - public static partial class Image + public abstract partial class Image { /// /// By reading the header on the provided file this calculates the images mime type. diff --git a/src/ImageSharp/Image.FromStream.cs b/src/ImageSharp/Image.FromStream.cs index 8848225f54..b16501b0f2 100644 --- a/src/ImageSharp/Image.FromStream.cs +++ b/src/ImageSharp/Image.FromStream.cs @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp /// /// Adds static methods allowing the creation of new image from a given stream. /// - public static partial class Image + public abstract partial class Image { /// /// By reading the header on the provided stream this calculates the images mime type. diff --git a/src/ImageSharp/Image.LoadPixelData.cs b/src/ImageSharp/Image.LoadPixelData.cs index 282f980865..eb7ce261f8 100644 --- a/src/ImageSharp/Image.LoadPixelData.cs +++ b/src/ImageSharp/Image.LoadPixelData.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp /// /// Adds static methods allowing the creation of new image from raw pixel data. /// - public static partial class Image + public abstract partial class Image { /// /// Create a new instance of the class from the raw data. diff --git a/src/ImageSharp/Image.WrapMemory.cs b/src/ImageSharp/Image.WrapMemory.cs index 3d788bb73a..49bc99e9ee 100644 --- a/src/ImageSharp/Image.WrapMemory.cs +++ b/src/ImageSharp/Image.WrapMemory.cs @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp /// /// Adds static methods allowing wrapping an existing memory area as an image. /// - public static partial class Image + public abstract partial class Image { /// /// Wraps an existing contiguous memory area of 'width' x 'height' pixels, diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index a124ddcacd..f780ca666a 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -10,7 +10,10 @@ $(packageversion) 0.0.1 - netcoreapp2.1;netstandard1.3;netstandard2.0;net472 + + + netcoreapp2.1 + true true SixLabors.ImageSharp diff --git a/src/ImageSharp/Image{TPixel}.cs b/src/ImageSharp/Image{TPixel}.cs index e37f3e4c1c..ea6d0beb25 100644 --- a/src/ImageSharp/Image{TPixel}.cs +++ b/src/ImageSharp/Image{TPixel}.cs @@ -128,10 +128,10 @@ namespace SixLabors.ImageSharp public PixelTypeInfo PixelType { get; } /// - public int Width => this.Frames.RootFrame.Width; + public override int Width => this.Frames.RootFrame.Width; /// - public int Height => this.Frames.RootFrame.Height; + public override int Height => this.Frames.RootFrame.Height; /// public ImageMetadata Metadata { get; } @@ -197,7 +197,7 @@ namespace SixLabors.ImageSharp } /// - public void Dispose() => this.Frames.Dispose(); + public override void Dispose() => this.Frames.Dispose(); internal override void AcceptVisitor(IImageVisitor visitor) { From 86cc38ad940e03a1944444c568f1df544effa673 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 4 May 2019 00:55:09 +0200 Subject: [PATCH 106/223] refactor the rest of the FilterProcessor code --- .../Processing/BrightnessExtensions.cs | 12 ++-- .../Processing/ColorBlindnessExtensions.cs | 31 ++++------ .../Processing/ContrastExtensions.cs | 14 ++--- .../Processing/GrayscaleExtensions.cs | 62 +++++++------------ src/ImageSharp/Processing/HueExtensions.cs | 14 ++--- src/ImageSharp/Processing/InvertExtensions.cs | 14 ++--- .../Processing/KodachromeExtensions.cs | 14 ++--- .../Processing/LomographExtensions.cs | 14 ++--- .../Processing/PolaroidExtensions.cs | 13 ++-- .../Convolution/EdgeDetector2DProcessor.cs | 2 +- .../EdgeDetectorCompassProcessor.cs | 2 +- .../Convolution/EdgeDetectorProcessor.cs | 2 +- .../Filters/AchromatomalyProcessor.cs | 6 +- .../Filters/AchromatopsiaProcessor.cs | 6 +- .../Processors/Filters/BrightnessProcessor.cs | 6 +- .../Processors/Filters/ContrastProcessor.cs | 6 +- .../Filters/DeuteranomalyProcessor.cs | 6 +- .../Filters/DeuteranopiaProcessor.cs | 6 +- .../Processors/Filters/FilterProcessor.cs | 2 +- .../Filters/FilterProcessorImplementation.cs | 8 +-- .../Filters/GrayscaleBt601Processor.cs | 6 +- .../Filters/GrayscaleBt709Processor.cs | 17 +++-- .../Processors/Filters/HueProcessor.cs | 5 +- .../Processors/Filters/InvertProcessor.cs | 6 +- .../Processors/Filters/KodachromeProcessor.cs | 6 +- .../Processors/Filters/LomographProcessor.cs | 19 ++---- .../LomographProcessorImplementation.cs | 26 ++++++++ .../Processors/Filters/PolaroidProcessor.cs | 22 +++++-- .../Filters/ProtanomalyProcessor.cs | 6 +- .../Processors/Filters/ProtanopiaProcessor.cs | 5 +- .../Processors/Filters/SaturateProcessor.cs | 6 +- .../Processors/Filters/SepiaProcessor.cs | 6 +- .../Filters/TritanomalyProcessor.cs | 6 +- .../Processors/Filters/TritanopiaProcessor.cs | 6 +- .../Processing/Processors/IImageProcessor.cs | 31 +++++++++- .../Processing/SaturateExtensions.cs | 14 ++--- src/ImageSharp/Processing/SepiaExtensions.cs | 22 +++---- 37 files changed, 210 insertions(+), 239 deletions(-) create mode 100644 src/ImageSharp/Processing/Processors/Filters/LomographProcessorImplementation.cs diff --git a/src/ImageSharp/Processing/BrightnessExtensions.cs b/src/ImageSharp/Processing/BrightnessExtensions.cs index 2f252ad305..4de4dc897b 100644 --- a/src/ImageSharp/Processing/BrightnessExtensions.cs +++ b/src/ImageSharp/Processing/BrightnessExtensions.cs @@ -19,13 +19,11 @@ namespace SixLabors.ImageSharp.Processing /// A value of 0 will create an image that is completely black. A value of 1 leaves the input unchanged. /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing brighter results. /// - /// The pixel format. /// The image this method extends. /// The proportion of the conversion. Must be greater than or equal to 0. /// The . - public static IImageProcessingContext Brightness(this IImageProcessingContext source, float amount) - where TPixel : struct, IPixel - => source.ApplyProcessor(new BrightnessProcessor(amount)); + public static IImageProcessingContext Brightness(this IImageProcessingContext source, float amount) + => source.ApplyProcessor(new BrightnessProcessor(amount)); /// /// Alters the brightness component of the image. @@ -34,15 +32,13 @@ namespace SixLabors.ImageSharp.Processing /// A value of 0 will create an image that is completely black. A value of 1 leaves the input unchanged. /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing brighter results. /// - /// The pixel format. /// The image this method extends. /// The proportion of the conversion. Must be greater than or equal to 0. /// /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext Brightness(this IImageProcessingContext source, float amount, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new BrightnessProcessor(amount), rectangle); + public static IImageProcessingContext Brightness(this IImageProcessingContext source, float amount, Rectangle rectangle) + => source.ApplyProcessor(new BrightnessProcessor(amount), rectangle); } } diff --git a/src/ImageSharp/Processing/ColorBlindnessExtensions.cs b/src/ImageSharp/Processing/ColorBlindnessExtensions.cs index 3316358954..000d47bd3e 100644 --- a/src/ImageSharp/Processing/ColorBlindnessExtensions.cs +++ b/src/ImageSharp/Processing/ColorBlindnessExtensions.cs @@ -16,49 +16,44 @@ namespace SixLabors.ImageSharp.Processing /// /// Applies the given colorblindness simulator to the image. /// - /// The pixel format. /// The image this method extends. /// The type of color blindness simulator to apply. /// The . - public static IImageProcessingContext ColorBlindness(this IImageProcessingContext source, ColorBlindnessMode colorBlindness) - where TPixel : struct, IPixel - => source.ApplyProcessor(GetProcessor(colorBlindness)); + public static IImageProcessingContext ColorBlindness(this IImageProcessingContext source, ColorBlindnessMode colorBlindness) + => source.ApplyProcessor(GetProcessor(colorBlindness)); /// /// Applies the given colorblindness simulator to the image. /// - /// The pixel format. /// The image this method extends. /// The type of color blindness simulator to apply. /// /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext ColorBlindness(this IImageProcessingContext source, ColorBlindnessMode colorBlindnessMode, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(GetProcessor(colorBlindnessMode), rectangle); + public static IImageProcessingContext ColorBlindness(this IImageProcessingContext source, ColorBlindnessMode colorBlindnessMode, Rectangle rectangle) + => source.ApplyProcessor(GetProcessor(colorBlindnessMode), rectangle); - private static IImageProcessor GetProcessor(ColorBlindnessMode colorBlindness) - where TPixel : struct, IPixel + private static IImageProcessor GetProcessor(ColorBlindnessMode colorBlindness) { switch (colorBlindness) { case ColorBlindnessMode.Achromatomaly: - return new AchromatomalyProcessor(); + return new AchromatomalyProcessor(); case ColorBlindnessMode.Achromatopsia: - return new AchromatopsiaProcessor(); + return new AchromatopsiaProcessor(); case ColorBlindnessMode.Deuteranomaly: - return new DeuteranomalyProcessor(); + return new DeuteranomalyProcessor(); case ColorBlindnessMode.Deuteranopia: - return new DeuteranopiaProcessor(); + return new DeuteranopiaProcessor(); case ColorBlindnessMode.Protanomaly: - return new ProtanomalyProcessor(); + return new ProtanomalyProcessor(); case ColorBlindnessMode.Protanopia: - return new ProtanopiaProcessor(); + return new ProtanopiaProcessor(); case ColorBlindnessMode.Tritanomaly: - return new TritanomalyProcessor(); + return new TritanomalyProcessor(); default: - return new TritanopiaProcessor(); + return new TritanopiaProcessor(); } } } diff --git a/src/ImageSharp/Processing/ContrastExtensions.cs b/src/ImageSharp/Processing/ContrastExtensions.cs index 776aa67518..b03844d93b 100644 --- a/src/ImageSharp/Processing/ContrastExtensions.cs +++ b/src/ImageSharp/Processing/ContrastExtensions.cs @@ -8,7 +8,7 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the alteration of the contrast component to the type. + /// Adds extensions that allow the alteration of the contrast component to the type. /// public static class ContrastExtensions { @@ -19,13 +19,11 @@ namespace SixLabors.ImageSharp.Processing /// A value of 0 will create an image that is completely gray. A value of 1 leaves the input unchanged. /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing results with more contrast. /// - /// The pixel format. /// The image this method extends. /// The proportion of the conversion. Must be greater than or equal to 0. /// The . - public static IImageProcessingContext Contrast(this IImageProcessingContext source, float amount) - where TPixel : struct, IPixel - => source.ApplyProcessor(new ContrastProcessor(amount)); + public static IImageProcessingContext Contrast(this IImageProcessingContext source, float amount) + => source.ApplyProcessor(new ContrastProcessor(amount)); /// /// Alters the contrast component of the image. @@ -34,15 +32,13 @@ namespace SixLabors.ImageSharp.Processing /// A value of 0 will create an image that is completely gray. A value of 1 leaves the input unchanged. /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing results with more contrast. /// - /// The pixel format. /// The image this method extends. /// The proportion of the conversion. Must be greater than or equal to 0. /// /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext Contrast(this IImageProcessingContext source, float amount, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new ContrastProcessor(amount), rectangle); + public static IImageProcessingContext Contrast(this IImageProcessingContext source, float amount, Rectangle rectangle) + => source.ApplyProcessor(new ContrastProcessor(amount), rectangle); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/GrayscaleExtensions.cs b/src/ImageSharp/Processing/GrayscaleExtensions.cs index 9ab664056b..d68a385e47 100644 --- a/src/ImageSharp/Processing/GrayscaleExtensions.cs +++ b/src/ImageSharp/Processing/GrayscaleExtensions.cs @@ -9,56 +9,48 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of grayscale toning to the type. + /// Adds extensions that allow the application of grayscale toning to the type. /// public static class GrayscaleExtensions { /// /// Applies grayscale toning to the image. /// - /// The pixel format. /// The image this method extends. - /// The . - public static IImageProcessingContext Grayscale(this IImageProcessingContext source) - where TPixel : struct, IPixel + /// The . + public static IImageProcessingContext Grayscale(this IImageProcessingContext source) => Grayscale(source, GrayscaleMode.Bt709); /// /// Applies grayscale toning to the image using the given amount. /// - /// The pixel format. /// The image this method extends. /// The proportion of the conversion. Must be between 0 and 1. - /// The . - public static IImageProcessingContext Grayscale(this IImageProcessingContext source, float amount) - where TPixel : struct, IPixel + /// The . + public static IImageProcessingContext Grayscale(this IImageProcessingContext source, float amount) => Grayscale(source, GrayscaleMode.Bt709, amount); /// /// Applies grayscale toning to the image with the given . /// - /// The pixel format. /// The image this method extends. /// The formula to apply to perform the operation. - /// The . - public static IImageProcessingContext Grayscale(this IImageProcessingContext source, GrayscaleMode mode) - where TPixel : struct, IPixel + /// The . + public static IImageProcessingContext Grayscale(this IImageProcessingContext source, GrayscaleMode mode) => Grayscale(source, mode, 1F); /// /// Applies grayscale toning to the image with the given using the given amount. /// - /// The pixel format. /// The image this method extends. /// The formula to apply to perform the operation. /// The proportion of the conversion. Must be between 0 and 1. - /// The . - public static IImageProcessingContext Grayscale(this IImageProcessingContext source, GrayscaleMode mode, float amount) - where TPixel : struct, IPixel + /// The . + public static IImageProcessingContext Grayscale(this IImageProcessingContext source, GrayscaleMode mode, float amount) { - IImageProcessor processor = mode == GrayscaleMode.Bt709 - ? (IImageProcessor)new GrayscaleBt709Processor(amount) - : new GrayscaleBt601Processor(1F); + IImageProcessor processor = mode == GrayscaleMode.Bt709 + ? (IImageProcessor)new GrayscaleBt709Processor(amount) + : new GrayscaleBt601Processor(1F); source.ApplyProcessor(processor); return source; @@ -67,61 +59,53 @@ namespace SixLabors.ImageSharp.Processing /// /// Applies grayscale toning to the image. /// - /// The pixel format. /// The image this method extends. /// /// The structure that specifies the portion of the image object to alter. /// - /// The . - public static IImageProcessingContext Grayscale(this IImageProcessingContext source, Rectangle rectangle) - where TPixel : struct, IPixel + /// The . + public static IImageProcessingContext Grayscale(this IImageProcessingContext source, Rectangle rectangle) => Grayscale(source, 1F, rectangle); /// /// Applies grayscale toning to the image using the given amount. /// - /// The pixel format. /// The image this method extends. /// The proportion of the conversion. Must be between 0 and 1. /// /// The structure that specifies the portion of the image object to alter. /// - /// The . - public static IImageProcessingContext Grayscale(this IImageProcessingContext source, float amount, Rectangle rectangle) - where TPixel : struct, IPixel + /// The . + public static IImageProcessingContext Grayscale(this IImageProcessingContext source, float amount, Rectangle rectangle) => Grayscale(source, GrayscaleMode.Bt709, amount, rectangle); /// /// Applies grayscale toning to the image. /// - /// The pixel format. /// The image this method extends. /// The formula to apply to perform the operation. /// /// The structure that specifies the portion of the image object to alter. /// - /// The . - public static IImageProcessingContext Grayscale(this IImageProcessingContext source, GrayscaleMode mode, Rectangle rectangle) - where TPixel : struct, IPixel + /// The . + public static IImageProcessingContext Grayscale(this IImageProcessingContext source, GrayscaleMode mode, Rectangle rectangle) => Grayscale(source, mode, 1F, rectangle); /// /// Applies grayscale toning to the image using the given amount. /// - /// The pixel format. /// The image this method extends. /// The formula to apply to perform the operation. /// The proportion of the conversion. Must be between 0 and 1. /// /// The structure that specifies the portion of the image object to alter. /// - /// The . - public static IImageProcessingContext Grayscale(this IImageProcessingContext source, GrayscaleMode mode, float amount, Rectangle rectangle) - where TPixel : struct, IPixel + /// The . + public static IImageProcessingContext Grayscale(this IImageProcessingContext source, GrayscaleMode mode, float amount, Rectangle rectangle) { - IImageProcessor processor = mode == GrayscaleMode.Bt709 - ? (IImageProcessor)new GrayscaleBt709Processor(amount) - : new GrayscaleBt601Processor(amount); + IImageProcessor processor = mode == GrayscaleMode.Bt709 + ? (IImageProcessor)new GrayscaleBt709Processor(amount) + : new GrayscaleBt601Processor(amount); source.ApplyProcessor(processor, rectangle); return source; diff --git a/src/ImageSharp/Processing/HueExtensions.cs b/src/ImageSharp/Processing/HueExtensions.cs index 246d4bf2bd..17296e1db5 100644 --- a/src/ImageSharp/Processing/HueExtensions.cs +++ b/src/ImageSharp/Processing/HueExtensions.cs @@ -8,33 +8,29 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the alteration of the hue component to the type. + /// Adds extensions that allow the alteration of the hue component to the type. /// public static class HueExtensions { /// /// Alters the hue component of the image. /// - /// The pixel format. /// The image this method extends. /// The rotation angle in degrees to adjust the hue. /// The . - public static IImageProcessingContext Hue(this IImageProcessingContext source, float degrees) - where TPixel : struct, IPixel - => source.ApplyProcessor(new HueProcessor(degrees)); + public static IImageProcessingContext Hue(this IImageProcessingContext source, float degrees) + => source.ApplyProcessor(new HueProcessor(degrees)); /// /// Alters the hue component of the image. /// - /// The pixel format. /// The image this method extends. /// The rotation angle in degrees to adjust the hue. /// /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext Hue(this IImageProcessingContext source, float degrees, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new HueProcessor(degrees), rectangle); + public static IImageProcessingContext Hue(this IImageProcessingContext source, float degrees, Rectangle rectangle) + => source.ApplyProcessor(new HueProcessor(degrees), rectangle); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/InvertExtensions.cs b/src/ImageSharp/Processing/InvertExtensions.cs index 9e031bc95a..828aaa1bbc 100644 --- a/src/ImageSharp/Processing/InvertExtensions.cs +++ b/src/ImageSharp/Processing/InvertExtensions.cs @@ -8,31 +8,27 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the inversion of colors to the type. + /// Adds extensions that allow the inversion of colors to the type. /// public static class InvertExtensions { /// /// Inverts the colors of the image. /// - /// The pixel format. /// The image this method extends. /// The . - public static IImageProcessingContext Invert(this IImageProcessingContext source) - where TPixel : struct, IPixel - => source.ApplyProcessor(new InvertProcessor(1F)); + public static IImageProcessingContext Invert(this IImageProcessingContext source) + => source.ApplyProcessor(new InvertProcessor(1F)); /// /// Inverts the colors of the image. /// - /// The pixel format. /// The image this method extends. /// /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext Invert(this IImageProcessingContext source, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new InvertProcessor(1F), rectangle); + public static IImageProcessingContext Invert(this IImageProcessingContext source, Rectangle rectangle) + => source.ApplyProcessor(new InvertProcessor(1F), rectangle); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/KodachromeExtensions.cs b/src/ImageSharp/Processing/KodachromeExtensions.cs index e438b131ed..24bd51e016 100644 --- a/src/ImageSharp/Processing/KodachromeExtensions.cs +++ b/src/ImageSharp/Processing/KodachromeExtensions.cs @@ -8,31 +8,27 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the recreation of an old Kodachrome camera effect to the type. + /// Adds extensions that allow the recreation of an old Kodachrome camera effect to the type. /// public static class KodachromeExtensions { /// /// Alters the colors of the image recreating an old Kodachrome camera effect. /// - /// The pixel format. /// The image this method extends. /// The . - public static IImageProcessingContext Kodachrome(this IImageProcessingContext source) - where TPixel : struct, IPixel - => source.ApplyProcessor(new KodachromeProcessor()); + public static IImageProcessingContext Kodachrome(this IImageProcessingContext source) + => source.ApplyProcessor(new KodachromeProcessor()); /// /// Alters the colors of the image recreating an old Kodachrome camera effect. /// - /// The pixel format. /// The image this method extends. /// /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext Kodachrome(this IImageProcessingContext source, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new KodachromeProcessor(), rectangle); + public static IImageProcessingContext Kodachrome(this IImageProcessingContext source, Rectangle rectangle) + => source.ApplyProcessor(new KodachromeProcessor(), rectangle); } } diff --git a/src/ImageSharp/Processing/LomographExtensions.cs b/src/ImageSharp/Processing/LomographExtensions.cs index 7dff164026..a3fd3d3b50 100644 --- a/src/ImageSharp/Processing/LomographExtensions.cs +++ b/src/ImageSharp/Processing/LomographExtensions.cs @@ -8,31 +8,27 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the recreation of an old Lomograph camera effect to the type. + /// Adds extensions that allow the recreation of an old Lomograph camera effect to the type. /// public static class LomographExtensions { /// /// Alters the colors of the image recreating an old Lomograph camera effect. /// - /// The pixel format. /// The image this method extends. /// The . - public static IImageProcessingContext Lomograph(this IImageProcessingContext source) - where TPixel : struct, IPixel - => source.ApplyProcessor(new LomographProcessor()); + public static IImageProcessingContext Lomograph(this IImageProcessingContext source) + => source.ApplyProcessor(new LomographProcessor()); /// /// Alters the colors of the image recreating an old Lomograph camera effect. /// - /// The pixel format. /// The image this method extends. /// /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext Lomograph(this IImageProcessingContext source, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new LomographProcessor(), rectangle); + public static IImageProcessingContext Lomograph(this IImageProcessingContext source, Rectangle rectangle) + => source.ApplyProcessor(new LomographProcessor(), rectangle); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/PolaroidExtensions.cs b/src/ImageSharp/Processing/PolaroidExtensions.cs index 5d4beee221..4172c1d615 100644 --- a/src/ImageSharp/Processing/PolaroidExtensions.cs +++ b/src/ImageSharp/Processing/PolaroidExtensions.cs @@ -8,19 +8,17 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the recreation of an old Polaroid camera effect to the type. + /// Adds extensions that allow the recreation of an old Polaroid camera effect to the type. /// public static class PolaroidExtensions { /// /// Alters the colors of the image recreating an old Polaroid camera effect. /// - /// The pixel format. /// The image this method extends. /// The . - public static IImageProcessingContext Polaroid(this IImageProcessingContext source) - where TPixel : struct, IPixel - => source.ApplyProcessor(new PolaroidProcessor()); + public static IImageProcessingContext Polaroid(this IImageProcessingContext source) + => source.ApplyProcessor(new PolaroidProcessor()); /// /// Alters the colors of the image recreating an old Polaroid camera effect. @@ -31,8 +29,7 @@ namespace SixLabors.ImageSharp.Processing /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext Polaroid(this IImageProcessingContext source, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new PolaroidProcessor(), rectangle); + public static IImageProcessingContext Polaroid(this IImageProcessingContext source, Rectangle rectangle) + => source.ApplyProcessor(new PolaroidProcessor(), rectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs index d2e9630dc0..c08a176618 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs @@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { if (this.Grayscale) { - new GrayscaleBt709Processor(1F).Apply(source, sourceRectangle, configuration); + new GrayscaleBt709Processor(1F).ApplyToFrame(source, sourceRectangle, configuration); } } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs index 73f92fae38..aa33699f5d 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs @@ -76,7 +76,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { if (this.Grayscale) { - new GrayscaleBt709Processor(1F).Apply(source, sourceRectangle, configuration); + new GrayscaleBt709Processor(1F).ApplyToFrame(source, sourceRectangle, configuration); } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor.cs index edc7ec4ccd..9ec79da9eb 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor.cs @@ -39,7 +39,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { if (this.Grayscale) { - new GrayscaleBt709Processor(1F).Apply(source, sourceRectangle, configuration); + new GrayscaleBt709Processor(1F).ApplyToFrame(source, sourceRectangle, configuration); } } diff --git a/src/ImageSharp/Processing/Processors/Filters/AchromatomalyProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/AchromatomalyProcessor.cs index 57c1bad39b..2dac6b827d 100644 --- a/src/ImageSharp/Processing/Processors/Filters/AchromatomalyProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/AchromatomalyProcessor.cs @@ -8,12 +8,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating Achromatomaly (Color desensitivity) color blindness. /// - /// The pixel format. - internal class AchromatomalyProcessor : FilterProcessor - where TPixel : struct, IPixel + internal class AchromatomalyProcessor : FilterProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public AchromatomalyProcessor() : base(KnownFilterMatrices.AchromatomalyFilter) diff --git a/src/ImageSharp/Processing/Processors/Filters/AchromatopsiaProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/AchromatopsiaProcessor.cs index 696a854ab8..4df94a7603 100644 --- a/src/ImageSharp/Processing/Processors/Filters/AchromatopsiaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/AchromatopsiaProcessor.cs @@ -8,12 +8,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating Achromatopsia (Monochrome) color blindness. /// - /// The pixel format. - internal class AchromatopsiaProcessor : FilterProcessor - where TPixel : struct, IPixel + internal class AchromatopsiaProcessor : FilterProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public AchromatopsiaProcessor() : base(KnownFilterMatrices.AchromatopsiaFilter) diff --git a/src/ImageSharp/Processing/Processors/Filters/BrightnessProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/BrightnessProcessor.cs index b1b8ad7478..6a1f3869ae 100644 --- a/src/ImageSharp/Processing/Processors/Filters/BrightnessProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/BrightnessProcessor.cs @@ -8,12 +8,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies a brightness filter matrix using the given amount. /// - /// The pixel format. - internal class BrightnessProcessor : FilterProcessor - where TPixel : struct, IPixel + internal class BrightnessProcessor : FilterProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// A value of 0 will create an image that is completely black. A value of 1 leaves the input unchanged. diff --git a/src/ImageSharp/Processing/Processors/Filters/ContrastProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/ContrastProcessor.cs index ebec464d5c..3bea695b99 100644 --- a/src/ImageSharp/Processing/Processors/Filters/ContrastProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/ContrastProcessor.cs @@ -8,12 +8,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies a contrast filter matrix using the given amount. /// - /// The pixel format. - internal class ContrastProcessor : FilterProcessor - where TPixel : struct, IPixel + internal class ContrastProcessor : FilterProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// A value of 0 will create an image that is completely gray. A value of 1 leaves the input unchanged. diff --git a/src/ImageSharp/Processing/Processors/Filters/DeuteranomalyProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/DeuteranomalyProcessor.cs index 0d1b1da902..395489d563 100644 --- a/src/ImageSharp/Processing/Processors/Filters/DeuteranomalyProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/DeuteranomalyProcessor.cs @@ -8,12 +8,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating Deuteranomaly (Green-Weak) color blindness. /// - /// The pixel format. - internal class DeuteranomalyProcessor : FilterProcessor - where TPixel : struct, IPixel + internal class DeuteranomalyProcessor : FilterProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public DeuteranomalyProcessor() : base(KnownFilterMatrices.DeuteranomalyFilter) diff --git a/src/ImageSharp/Processing/Processors/Filters/DeuteranopiaProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/DeuteranopiaProcessor.cs index ae0727048e..51f8361e6c 100644 --- a/src/ImageSharp/Processing/Processors/Filters/DeuteranopiaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/DeuteranopiaProcessor.cs @@ -8,12 +8,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating Deuteranopia (Green-Blind) color blindness. /// - /// The pixel format. - internal class DeuteranopiaProcessor : FilterProcessor - where TPixel : struct, IPixel + internal class DeuteranopiaProcessor : FilterProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public DeuteranopiaProcessor() : base(KnownFilterMatrices.DeuteranopiaFilter) diff --git a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs index 3f9f586877..5637e97700 100644 --- a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// public ColorMatrix Matrix { get; } - public IImageProcessor CreatePixelSpecificProcessor() + public virtual IImageProcessor CreatePixelSpecificProcessor() where TPixel : struct, IPixel { return new FilterProcessorImplementation(this); diff --git a/src/ImageSharp/Processing/Processors/Filters/FilterProcessorImplementation.cs b/src/ImageSharp/Processing/Processors/Filters/FilterProcessorImplementation.cs index 1eaaf1ea5b..8fea81564c 100644 --- a/src/ImageSharp/Processing/Processors/Filters/FilterProcessorImplementation.cs +++ b/src/ImageSharp/Processing/Processors/Filters/FilterProcessorImplementation.cs @@ -18,11 +18,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters internal class FilterProcessorImplementation : ImageProcessor where TPixel : struct, IPixel { - private readonly FilterProcessor paramterSource; + private readonly FilterProcessor definition; - public FilterProcessorImplementation(FilterProcessor paramterSource) + public FilterProcessorImplementation(FilterProcessor definition) { - this.paramterSource = paramterSource; + this.definition = definition; } /// @@ -31,7 +31,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); int startX = interest.X; - ColorMatrix matrix = this.paramterSource.Matrix; + ColorMatrix matrix = this.definition.Matrix; ParallelHelper.IterateRowsWithTempBuffer( interest, diff --git a/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt601Processor.cs b/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt601Processor.cs index c933d4858f..b39e01b9a8 100644 --- a/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt601Processor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt601Processor.cs @@ -8,12 +8,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies a grayscale filter matrix using the given amount and the formula as specified by ITU-R Recommendation BT.601 /// - /// The pixel format. - internal class GrayscaleBt601Processor : FilterProcessor - where TPixel : struct, IPixel + internal class GrayscaleBt601Processor : FilterProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The proportion of the conversion. Must be between 0 and 1. public GrayscaleBt601Processor(float amount) diff --git a/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs b/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs index 1716773b4c..038a2885bc 100644 --- a/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs @@ -2,18 +2,17 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// /// Applies a grayscale filter matrix using the given amount and the formula as specified by ITU-R Recommendation BT.709 /// - /// The pixel format. - internal class GrayscaleBt709Processor : FilterProcessor - where TPixel : struct, IPixel + internal class GrayscaleBt709Processor : FilterProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The proportion of the conversion. Must be between 0 and 1. public GrayscaleBt709Processor(float amount) @@ -23,8 +22,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters } /// - /// Gets the proportion of the conversion + /// Gets the proportion of the conversion. /// public float Amount { get; } + + // TODO: Move this to an appropriate extension method if possible. + internal void ApplyToFrame(ImageFrame frame, Rectangle sourceRectangle, Configuration configuration) + where TPixel : struct, IPixel + { + var processorImpl = new FilterProcessorImplementation(new GrayscaleBt709Processor(1F)); + processorImpl.Apply(frame, sourceRectangle, configuration); + } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/HueProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/HueProcessor.cs index 4c3a0c73ed..a69fd50757 100644 --- a/src/ImageSharp/Processing/Processors/Filters/HueProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/HueProcessor.cs @@ -8,11 +8,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies a hue filter matrix using the given angle of rotation in degrees /// - internal class HueProcessor : FilterProcessor - where TPixel : struct, IPixel + internal class HueProcessor : FilterProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The angle of rotation in degrees public HueProcessor(float degrees) diff --git a/src/ImageSharp/Processing/Processors/Filters/InvertProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/InvertProcessor.cs index 462c420707..d7631970db 100644 --- a/src/ImageSharp/Processing/Processors/Filters/InvertProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/InvertProcessor.cs @@ -8,12 +8,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies a filter matrix that inverts the colors of an image /// - /// The pixel format. - internal class InvertProcessor : FilterProcessor - where TPixel : struct, IPixel + internal class InvertProcessor : FilterProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The proportion of the conversion. Must be between 0 and 1. public InvertProcessor(float amount) diff --git a/src/ImageSharp/Processing/Processors/Filters/KodachromeProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/KodachromeProcessor.cs index 003766e8ab..5570f23d7d 100644 --- a/src/ImageSharp/Processing/Processors/Filters/KodachromeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/KodachromeProcessor.cs @@ -8,12 +8,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies a filter matrix recreating an old Kodachrome camera effect matrix to the image /// - /// The pixel format. - internal class KodachromeProcessor : FilterProcessor - where TPixel : struct, IPixel + internal class KodachromeProcessor : FilterProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public KodachromeProcessor() : base(KnownFilterMatrices.KodachromeFilter) diff --git a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs index 737ebf6188..345d829ac6 100644 --- a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs @@ -1,33 +1,22 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing.Processors.Overlays; -using SixLabors.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// /// Converts the colors of the image recreating an old Lomograph effect. /// - /// The pixel format. - internal class LomographProcessor : FilterProcessor - where TPixel : struct, IPixel + internal class LomographProcessor : FilterProcessor { - private static readonly TPixel VeryDarkGreen = ColorBuilder.FromRGBA(0, 10, 0, 255); - /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public LomographProcessor() : base(KnownFilterMatrices.LomographFilter) { } - /// - protected override void AfterFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - { - new VignetteProcessor(VeryDarkGreen).Apply(source, sourceRectangle, configuration); - } + public override IImageProcessor CreatePixelSpecificProcessor() => + new LomographProcessorImplementation(this); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/LomographProcessorImplementation.cs b/src/ImageSharp/Processing/Processors/Filters/LomographProcessorImplementation.cs new file mode 100644 index 0000000000..d851b98e06 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Filters/LomographProcessorImplementation.cs @@ -0,0 +1,26 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors.Overlays; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Filters +{ + internal class LomographProcessorImplementation : FilterProcessorImplementation + where TPixel : struct, IPixel + { + private static readonly TPixel VeryDarkGreen = ColorBuilder.FromRGBA(0, 10, 0, 255); + + /// + protected override void AfterFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + new VignetteProcessor(VeryDarkGreen).Apply(source, sourceRectangle, configuration); + } + + public LomographProcessorImplementation(FilterProcessor definition) + : base(definition) + { + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs index fb065ac176..af16d9eab2 100644 --- a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs @@ -10,13 +10,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating an old Polaroid effect. /// - /// The pixel format. - internal class PolaroidProcessor : FilterProcessor - where TPixel : struct, IPixel + public class PolaroidProcessor : FilterProcessor { - private static readonly TPixel VeryDarkOrange = ColorBuilder.FromRGB(102, 34, 0); - private static readonly TPixel LightOrange = ColorBuilder.FromRGBA(255, 153, 102, 128); - /// /// Initializes a new instance of the class. /// @@ -25,11 +20,26 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters { } + public override IImageProcessor CreatePixelSpecificProcessor() => + new PolaroidProcessorImplementation(this); + } + + internal class PolaroidProcessorImplementation : FilterProcessorImplementation + where TPixel : struct, IPixel + { + private static readonly TPixel VeryDarkOrange = ColorBuilder.FromRGB(102, 34, 0); + private static readonly TPixel LightOrange = ColorBuilder.FromRGBA(255, 153, 102, 128); + /// protected override void AfterFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { new VignetteProcessor(VeryDarkOrange).Apply(source, sourceRectangle, configuration); new GlowProcessor(LightOrange, source.Width / 4F).Apply(source, sourceRectangle, configuration); } + + public PolaroidProcessorImplementation(FilterProcessor definition) + : base(definition) + { + } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/ProtanomalyProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/ProtanomalyProcessor.cs index 79eb708518..95e369b371 100644 --- a/src/ImageSharp/Processing/Processors/Filters/ProtanomalyProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/ProtanomalyProcessor.cs @@ -8,12 +8,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating Protanomaly (Red-Weak) color blindness. /// - /// The pixel format. - internal class ProtanomalyProcessor : FilterProcessor - where TPixel : struct, IPixel + internal class ProtanomalyProcessor : FilterProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public ProtanomalyProcessor() : base(KnownFilterMatrices.ProtanomalyFilter) diff --git a/src/ImageSharp/Processing/Processors/Filters/ProtanopiaProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/ProtanopiaProcessor.cs index c6a01439a2..94eeaae475 100644 --- a/src/ImageSharp/Processing/Processors/Filters/ProtanopiaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/ProtanopiaProcessor.cs @@ -9,11 +9,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// Converts the colors of the image recreating Protanopia (Red-Blind) color blindness. /// /// The pixel format. - internal class ProtanopiaProcessor : FilterProcessor - where TPixel : struct, IPixel + internal class ProtanopiaProcessor : FilterProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public ProtanopiaProcessor() : base(KnownFilterMatrices.ProtanopiaFilter) diff --git a/src/ImageSharp/Processing/Processors/Filters/SaturateProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/SaturateProcessor.cs index 75e956071e..424eeb461c 100644 --- a/src/ImageSharp/Processing/Processors/Filters/SaturateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/SaturateProcessor.cs @@ -8,12 +8,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies a saturation filter matrix using the given amount. /// - /// The pixel format. - internal class SaturateProcessor : FilterProcessor - where TPixel : struct, IPixel + internal class SaturateProcessor : FilterProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// A value of 0 is completely un-saturated. A value of 1 leaves the input unchanged. diff --git a/src/ImageSharp/Processing/Processors/Filters/SepiaProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/SepiaProcessor.cs index 2009dccd56..538db4b52c 100644 --- a/src/ImageSharp/Processing/Processors/Filters/SepiaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/SepiaProcessor.cs @@ -8,12 +8,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies a sepia filter matrix using the given amount. /// - /// The pixel format. - internal class SepiaProcessor : FilterProcessor - where TPixel : struct, IPixel + internal class SepiaProcessor : FilterProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The proportion of the conversion. Must be between 0 and 1. public SepiaProcessor(float amount) diff --git a/src/ImageSharp/Processing/Processors/Filters/TritanomalyProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/TritanomalyProcessor.cs index 593f7f5b01..3eda05a115 100644 --- a/src/ImageSharp/Processing/Processors/Filters/TritanomalyProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/TritanomalyProcessor.cs @@ -8,12 +8,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating Tritanomaly (Blue-Weak) color blindness. /// - /// The pixel format. - internal class TritanomalyProcessor : FilterProcessor - where TPixel : struct, IPixel + internal class TritanomalyProcessor : FilterProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public TritanomalyProcessor() : base(KnownFilterMatrices.TritanomalyFilter) diff --git a/src/ImageSharp/Processing/Processors/Filters/TritanopiaProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/TritanopiaProcessor.cs index 153ad5559a..f5ed26c833 100644 --- a/src/ImageSharp/Processing/Processors/Filters/TritanopiaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/TritanopiaProcessor.cs @@ -8,12 +8,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating Tritanopia (Blue-Blind) color blindness. /// - /// The pixel format. - internal class TritanopiaProcessor : FilterProcessor - where TPixel : struct, IPixel + internal class TritanopiaProcessor : FilterProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public TritanopiaProcessor() : base(KnownFilterMatrices.TritanopiaFilter) diff --git a/src/ImageSharp/Processing/Processors/IImageProcessor.cs b/src/ImageSharp/Processing/Processors/IImageProcessor.cs index 78800affbc..68db453806 100644 --- a/src/ImageSharp/Processing/Processors/IImageProcessor.cs +++ b/src/ImageSharp/Processing/Processors/IImageProcessor.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Processing.Processors IImageProcessor CreatePixelSpecificProcessor() where TPixel : struct, IPixel; } - + /// /// Encapsulates methods to alter the pixels of an image. /// @@ -34,4 +34,33 @@ namespace SixLabors.ImageSharp.Processing.Processors /// void Apply(Image source, Rectangle sourceRectangle); } + + internal static class ImageProcessorExtensions + { + public static void Apply(this IImageProcessor processor, Image source, Rectangle sourceRectangle) + { + var visitor = new ApplyVisitor(processor, sourceRectangle); + source.AcceptVisitor(visitor); + } + + private class ApplyVisitor : IImageVisitor + { + private readonly IImageProcessor processor; + + private readonly Rectangle sourceRectangle; + + public ApplyVisitor(IImageProcessor processor, Rectangle sourceRectangle) + { + this.processor = processor; + this.sourceRectangle = sourceRectangle; + } + + public void Visit(Image image) + where TPixel : struct, IPixel + { + var processorImpl = processor.CreatePixelSpecificProcessor(); + processorImpl.Apply(image, this.sourceRectangle); + } + } + } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/SaturateExtensions.cs b/src/ImageSharp/Processing/SaturateExtensions.cs index ba45ae12c9..fe2cf5f348 100644 --- a/src/ImageSharp/Processing/SaturateExtensions.cs +++ b/src/ImageSharp/Processing/SaturateExtensions.cs @@ -8,7 +8,7 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the alteration of the saturation component to the type. + /// Adds extensions that allow the alteration of the saturation component to the type. /// public static class SaturateExtensions { @@ -19,13 +19,11 @@ namespace SixLabors.ImageSharp.Processing /// A value of 0 is completely un-saturated. A value of 1 leaves the input unchanged. /// Other values are linear multipliers on the effect. Values of amount over 1 are allowed, providing super-saturated results /// - /// The pixel format. /// The image this method extends. /// The proportion of the conversion. Must be greater than or equal to 0. /// The . - public static IImageProcessingContext Saturate(this IImageProcessingContext source, float amount) - where TPixel : struct, IPixel - => source.ApplyProcessor(new SaturateProcessor(amount)); + public static IImageProcessingContext Saturate(this IImageProcessingContext source, float amount) + => source.ApplyProcessor(new SaturateProcessor(amount)); /// /// Alters the saturation component of the image. @@ -34,15 +32,13 @@ namespace SixLabors.ImageSharp.Processing /// A value of 0 is completely un-saturated. A value of 1 leaves the input unchanged. /// Other values are linear multipliers on the effect. Values of amount over 1 are allowed, providing super-saturated results /// - /// The pixel format. /// The image this method extends. /// The proportion of the conversion. Must be greater than or equal to 0. /// /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext Saturate(this IImageProcessingContext source, float amount, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new SaturateProcessor(amount), rectangle); + public static IImageProcessingContext Saturate(this IImageProcessingContext source, float amount, Rectangle rectangle) + => source.ApplyProcessor(new SaturateProcessor(amount), rectangle); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/SepiaExtensions.cs b/src/ImageSharp/Processing/SepiaExtensions.cs index 08676ee62a..5b23ec11af 100644 --- a/src/ImageSharp/Processing/SepiaExtensions.cs +++ b/src/ImageSharp/Processing/SepiaExtensions.cs @@ -8,56 +8,48 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of sepia toning to the type. + /// Adds extensions that allow the application of sepia toning to the type. /// public static class SepiaExtensions { /// /// Applies sepia toning to the image. /// - /// The pixel format. /// The image this method extends. /// The . - public static IImageProcessingContext Sepia(this IImageProcessingContext source) - where TPixel : struct, IPixel + public static IImageProcessingContext Sepia(this IImageProcessingContext source) => Sepia(source, 1F); /// /// Applies sepia toning to the image using the given amount. /// - /// The pixel format. /// The image this method extends. /// The proportion of the conversion. Must be between 0 and 1. /// The . - public static IImageProcessingContext Sepia(this IImageProcessingContext source, float amount) - where TPixel : struct, IPixel - => source.ApplyProcessor(new SepiaProcessor(amount)); + public static IImageProcessingContext Sepia(this IImageProcessingContext source, float amount) + => source.ApplyProcessor(new SepiaProcessor(amount)); /// /// Applies sepia toning to the image. /// - /// The pixel format. /// The image this method extends. /// /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext Sepia(this IImageProcessingContext source, Rectangle rectangle) - where TPixel : struct, IPixel + public static IImageProcessingContext Sepia(this IImageProcessingContext source, Rectangle rectangle) => Sepia(source, 1F, rectangle); /// /// Applies sepia toning to the image. /// - /// The pixel format. /// The image this method extends. /// The proportion of the conversion. Must be between 0 and 1. /// /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext Sepia(this IImageProcessingContext source, float amount, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new SepiaProcessor(amount), rectangle); + public static IImageProcessingContext Sepia(this IImageProcessingContext source, float amount, Rectangle rectangle) + => source.ApplyProcessor(new SepiaProcessor(amount), rectangle); } } \ No newline at end of file From 59845dcbd7d64756c6b9dc9ae81edf3dbf09fc6b Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 4 May 2019 01:08:48 +0200 Subject: [PATCH 107/223] reached a fully compiling state --- .../ImageSharp.Drawing.csproj | 4 +- .../Formats/Gif/GifEncoderTests.cs | 6 +-- .../Formats/Jpg/JpegDecoderTests.cs | 4 +- .../ImageSharp.Tests/ImageSharp.Tests.csproj | 3 +- .../Processing/Filters/BlackWhiteTest.cs | 27 ----------- .../Processing/Filters/BrightnessTest.cs | 31 ------------ .../Processing/Filters/ColorBlindnessTest.cs | 46 ------------------ .../Processing/Filters/ContrastTest.cs | 33 ------------- .../Processing/Filters/FilterTest.cs | 4 +- .../Processing/Filters/GrayscaleTest.cs | 47 ------------------- .../Processing/Filters/HueTest.cs | 32 ------------- .../Processing/Filters/InvertTest.cs | 27 ----------- .../Processing/Filters/KodachromeTest.cs | 27 ----------- .../Processing/Filters/LomographTest.cs | 31 ------------ .../Processing/Filters/OpacityTest.cs | 31 ------------ .../Processing/Filters/PolaroidTest.cs | 28 ----------- .../Processing/Filters/SaturateTest.cs | 31 ------------ .../Processing/Filters/SepiaTest.cs | 27 ----------- .../Processing/Transforms/PadTest.cs | 2 +- .../Processing/Transforms/ResizeTests.cs | 8 ++-- 20 files changed, 17 insertions(+), 432 deletions(-) delete mode 100644 tests/ImageSharp.Tests/Processing/Filters/BlackWhiteTest.cs delete mode 100644 tests/ImageSharp.Tests/Processing/Filters/BrightnessTest.cs delete mode 100644 tests/ImageSharp.Tests/Processing/Filters/ColorBlindnessTest.cs delete mode 100644 tests/ImageSharp.Tests/Processing/Filters/ContrastTest.cs delete mode 100644 tests/ImageSharp.Tests/Processing/Filters/GrayscaleTest.cs delete mode 100644 tests/ImageSharp.Tests/Processing/Filters/HueTest.cs delete mode 100644 tests/ImageSharp.Tests/Processing/Filters/InvertTest.cs delete mode 100644 tests/ImageSharp.Tests/Processing/Filters/KodachromeTest.cs delete mode 100644 tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs delete mode 100644 tests/ImageSharp.Tests/Processing/Filters/OpacityTest.cs delete mode 100644 tests/ImageSharp.Tests/Processing/Filters/PolaroidTest.cs delete mode 100644 tests/ImageSharp.Tests/Processing/Filters/SaturateTest.cs delete mode 100644 tests/ImageSharp.Tests/Processing/Filters/SepiaTest.cs diff --git a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj index 6f5cabb09b..1734c48de2 100644 --- a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj +++ b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj @@ -10,7 +10,9 @@ $(packageversion) 0.0.1 - netcoreapp2.1;netstandard1.3;netstandard2.0 + + netcoreapp2.1 + 7.3 true true diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs index cac4030d5f..5da1c3bb78 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs @@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif // Compare encoded result string path = provider.Utility.GetTestOutputFileName("gif", null, true); - using (var encoded = Image.Load(path)) + using (var encoded = Image.Load(path)) { encoded.CompareToReferenceOutput(ValidatorComparer, provider, null, "gif"); } @@ -180,7 +180,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif { inStream.Position = 0; - var image = Image.Load(inStream); + var image = Image.Load(inStream); GifMetadata metaData = image.Metadata.GetFormatMetadata(GifFormat.Instance); GifFrameMetadata frameMetaData = image.Frames.RootFrame.Metadata.GetFormatMetadata(GifFormat.Instance); GifColorTableMode colorMode = metaData.ColorTableMode; @@ -194,7 +194,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif outStream.Position = 0; outStream.Position = 0; - var clone = Image.Load(outStream); + var clone = Image.Load(outStream); GifMetadata cloneMetaData = clone.Metadata.GetFormatMetadata(GifFormat.Instance); Assert.Equal(metaData.ColorTableMode, cloneMetaData.ColorTableMode); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index 15f7f92a83..e316fe67eb 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -147,8 +147,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg var comparer = ImageComparer.Tolerant(0, 0); using (Image expectedImage = provider.GetReferenceOutputImage(appendPixelTypeToFileName: false)) - using (var pdfJsOriginalResult = Image.Load(pdfJsOriginalResultPath)) - using (var pdfJsPortResult = Image.Load(sourceBytes, JpegDecoder)) + using (var pdfJsOriginalResult = Image.Load(pdfJsOriginalResultPath)) + using (var pdfJsPortResult = Image.Load(sourceBytes, JpegDecoder)) { ImageSimilarityReport originalReport = comparer.CompareImagesOrFrames(expectedImage, pdfJsOriginalResult); ImageSimilarityReport portReport = comparer.CompareImagesOrFrames(expectedImage, pdfJsPortResult); diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index 0b727f30ce..efdf89964c 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -1,6 +1,7 @@  - netcoreapp2.1;net462;net472 + + netcoreapp2.1 True latest full diff --git a/tests/ImageSharp.Tests/Processing/Filters/BlackWhiteTest.cs b/tests/ImageSharp.Tests/Processing/Filters/BlackWhiteTest.cs deleted file mode 100644 index d651f2f04e..0000000000 --- a/tests/ImageSharp.Tests/Processing/Filters/BlackWhiteTest.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors.Filters; -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Processing.Filters -{ - public class BlackWhiteTest : BaseImageOperationsExtensionTest - { - [Fact] - public void BlackWhite_CorrectProcessor() - { - this.operations.BlackWhite(); - BlackWhiteProcessor p = this.Verify>(); - } - - [Fact] - public void BlackWhite_rect_CorrectProcessor() - { - this.operations.BlackWhite(this.rect); - BlackWhiteProcessor p = this.Verify>(this.rect); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/BrightnessTest.cs b/tests/ImageSharp.Tests/Processing/Filters/BrightnessTest.cs deleted file mode 100644 index e210450a8c..0000000000 --- a/tests/ImageSharp.Tests/Processing/Filters/BrightnessTest.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors.Filters; -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Processing.Effects -{ - public class BrightnessTest : BaseImageOperationsExtensionTest - { - [Fact] - public void Brightness_amount_BrightnessProcessorDefaultsSet() - { - this.operations.Brightness(1.5F); - BrightnessProcessor processor = this.Verify>(); - - Assert.Equal(1.5F, processor.Amount); - } - - [Fact] - public void Brightness_amount_rect_BrightnessProcessorDefaultsSet() - { - this.operations.Brightness(1.5F, this.rect); - BrightnessProcessor processor = this.Verify>(this.rect); - - Assert.Equal(1.5F, processor.Amount); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/ColorBlindnessTest.cs b/tests/ImageSharp.Tests/Processing/Filters/ColorBlindnessTest.cs deleted file mode 100644 index aeafe5fe1d..0000000000 --- a/tests/ImageSharp.Tests/Processing/Filters/ColorBlindnessTest.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Collections.Generic; - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors.Filters; -using SixLabors.ImageSharp.Processing.Processors; -using SixLabors.ImageSharp.Tests.TestUtilities; - -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Processing.Filters -{ - public class ColorBlindnessTest : BaseImageOperationsExtensionTest - { - public static IEnumerable TheoryData = new[] { - new object[]{ new TestType>(), ColorBlindnessMode.Achromatomaly }, - new object[]{ new TestType>(), ColorBlindnessMode.Achromatopsia }, - new object[]{ new TestType>(), ColorBlindnessMode.Deuteranomaly }, - new object[]{ new TestType>(), ColorBlindnessMode.Deuteranopia }, - new object[]{ new TestType>(), ColorBlindnessMode.Protanomaly }, - new object[]{ new TestType>(), ColorBlindnessMode.Protanopia }, - new object[]{ new TestType>(), ColorBlindnessMode.Tritanomaly }, - new object[]{ new TestType>(), ColorBlindnessMode.Tritanopia } - }; - - [Theory] - [MemberData(nameof(TheoryData))] - public void ColorBlindness_CorrectProcessor(TestType testType, ColorBlindnessMode colorBlindness) - where T : IImageProcessor - { - this.operations.ColorBlindness(colorBlindness); - T p = this.Verify(); - } - [Theory] - [MemberData(nameof(TheoryData))] - public void ColorBlindness_rect_CorrectProcessor(TestType testType, ColorBlindnessMode colorBlindness) - where T : IImageProcessor - { - this.operations.ColorBlindness(colorBlindness, this.rect); - T p = this.Verify(this.rect); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/ContrastTest.cs b/tests/ImageSharp.Tests/Processing/Filters/ContrastTest.cs deleted file mode 100644 index 21a552e6af..0000000000 --- a/tests/ImageSharp.Tests/Processing/Filters/ContrastTest.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing.Processors; -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Processing.Effects -{ - using SixLabors.ImageSharp.Processing; - using SixLabors.ImageSharp.Processing.Processors.Filters; - - public class ContrastTest : BaseImageOperationsExtensionTest - { - [Fact] - public void Contrast_amount_ContrastProcessorDefaultsSet() - { - this.operations.Contrast(1.5F); - ContrastProcessor processor = this.Verify>(); - - Assert.Equal(1.5F, processor.Amount); - } - - [Fact] - public void Contrast_amount_rect_ContrastProcessorDefaultsSet() - { - this.operations.Contrast(1.5F, this.rect); - ContrastProcessor processor = this.Verify>(this.rect); - - Assert.Equal(1.5F, processor.Amount); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/FilterTest.cs b/tests/ImageSharp.Tests/Processing/Filters/FilterTest.cs index 414a0d74e4..f4efb12748 100644 --- a/tests/ImageSharp.Tests/Processing/Filters/FilterTest.cs +++ b/tests/ImageSharp.Tests/Processing/Filters/FilterTest.cs @@ -17,14 +17,14 @@ namespace SixLabors.ImageSharp.Tests.Processing.Filters public void Filter_CorrectProcessor() { this.operations.Filter(KnownFilterMatrices.AchromatomalyFilter * KnownFilterMatrices.CreateHueFilter(90F)); - FilterProcessor p = this.Verify>(); + FilterProcessorImplementation p = this.Verify>(); } [Fact] public void Filter_rect_CorrectProcessor() { this.operations.Filter(KnownFilterMatrices.AchromatomalyFilter * KnownFilterMatrices.CreateHueFilter(90F), this.rect); - FilterProcessor p = this.Verify>(this.rect); + FilterProcessorImplementation p = this.Verify>(this.rect); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/GrayscaleTest.cs b/tests/ImageSharp.Tests/Processing/Filters/GrayscaleTest.cs deleted file mode 100644 index d63d978207..0000000000 --- a/tests/ImageSharp.Tests/Processing/Filters/GrayscaleTest.cs +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Collections.Generic; - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors.Filters; -using SixLabors.ImageSharp.Processing.Processors; -using SixLabors.ImageSharp.Tests.TestUtilities; - -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Processing.Filters -{ - public class GrayscaleTest : BaseImageOperationsExtensionTest - { - public static IEnumerable ModeTheoryData = new[] { - new object[]{ new TestType>(), GrayscaleMode.Bt709 } - }; - - [Theory] - [MemberData(nameof(ModeTheoryData))] - public void Grayscale_mode_CorrectProcessor(TestType testType, GrayscaleMode mode) - where T : IImageProcessor - { - this.operations.Grayscale(mode); - var p = this.Verify(); - } - - [Theory] - [MemberData(nameof(ModeTheoryData))] - public void Grayscale_mode_rect_CorrectProcessor(TestType testType, GrayscaleMode mode) - where T : IImageProcessor - { - this.operations.Grayscale(mode, this.rect); - this.Verify(this.rect); - } - - [Fact] - public void Grayscale_rect_CorrectProcessor() - { - this.operations.Grayscale(this.rect); - this.Verify>(this.rect); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/HueTest.cs b/tests/ImageSharp.Tests/Processing/Filters/HueTest.cs deleted file mode 100644 index f56578dd68..0000000000 --- a/tests/ImageSharp.Tests/Processing/Filters/HueTest.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors.Filters; - -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Processing.Filters -{ - public class HueTest : BaseImageOperationsExtensionTest - { - [Fact] - public void Hue_amount_HueProcessorDefaultsSet() - { - this.operations.Hue(34f); - var processor = this.Verify>(); - - Assert.Equal(34f, processor.Degrees); - } - - [Fact] - public void Hue_amount_rect_HueProcessorDefaultsSet() - { - this.operations.Hue(5f, this.rect); - var processor = this.Verify>(this.rect); - - Assert.Equal(5f, processor.Degrees); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/InvertTest.cs b/tests/ImageSharp.Tests/Processing/Filters/InvertTest.cs deleted file mode 100644 index c93afc9427..0000000000 --- a/tests/ImageSharp.Tests/Processing/Filters/InvertTest.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors.Filters; -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Processing.Effects -{ - public class InvertTest : BaseImageOperationsExtensionTest - { - [Fact] - public void Invert_InvertProcessorDefaultsSet() - { - this.operations.Invert(); - var processor = this.Verify>(); - } - - [Fact] - public void Pixelate_rect_PixelateProcessorDefaultsSet() - { - this.operations.Invert(this.rect); - var processor = this.Verify>(this.rect); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/KodachromeTest.cs b/tests/ImageSharp.Tests/Processing/Filters/KodachromeTest.cs deleted file mode 100644 index a982521404..0000000000 --- a/tests/ImageSharp.Tests/Processing/Filters/KodachromeTest.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors.Filters; -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Processing.Filters -{ - public class KodachromeTest : BaseImageOperationsExtensionTest - { - [Fact] - public void Kodachrome_amount_KodachromeProcessorDefaultsSet() - { - this.operations.Kodachrome(); - var processor = this.Verify>(); - } - - [Fact] - public void Kodachrome_amount_rect_KodachromeProcessorDefaultsSet() - { - this.operations.Kodachrome(this.rect); - var processor = this.Verify>(this.rect); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs b/tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs deleted file mode 100644 index c104f8c252..0000000000 --- a/tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.IO; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing.Processors; -using SixLabors.Primitives; -using Xunit; - -namespace SixLabors.ImageSharp.Tests -{ - using SixLabors.ImageSharp.Processing; - using SixLabors.ImageSharp.Processing.Processors.Filters; - - public class LomographTest : BaseImageOperationsExtensionTest - { - [Fact] - public void Lomograph_amount_LomographProcessorDefaultsSet() - { - this.operations.Lomograph(); - var processor = this.Verify>(); - } - - [Fact] - public void Lomograph_amount_rect_LomographProcessorDefaultsSet() - { - this.operations.Lomograph(this.rect); - var processor = this.Verify>(this.rect); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/OpacityTest.cs b/tests/ImageSharp.Tests/Processing/Filters/OpacityTest.cs deleted file mode 100644 index adbb8cf295..0000000000 --- a/tests/ImageSharp.Tests/Processing/Filters/OpacityTest.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors.Filters; -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Processing.Effects -{ - public class OpacityTest : BaseImageOperationsExtensionTest - { - [Fact] - public void Alpha_amount_AlphaProcessorDefaultsSet() - { - this.operations.Opacity(0.2f); - OpacityProcessor processor = this.Verify>(); - - Assert.Equal(.2f, processor.Amount); - } - - [Fact] - public void Alpha_amount_rect_AlphaProcessorDefaultsSet() - { - this.operations.Opacity(0.6f, this.rect); - OpacityProcessor processor = this.Verify>(this.rect); - - Assert.Equal(.6f, processor.Amount); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/PolaroidTest.cs b/tests/ImageSharp.Tests/Processing/Filters/PolaroidTest.cs deleted file mode 100644 index f28827b716..0000000000 --- a/tests/ImageSharp.Tests/Processing/Filters/PolaroidTest.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors.Filters; - -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Processing.Filters -{ - public class PolaroidTest : BaseImageOperationsExtensionTest - { - [Fact] - public void Polaroid_amount_PolaroidProcessorDefaultsSet() - { - this.operations.Polaroid(); - var processor = this.Verify>(); - } - - [Fact] - public void Polaroid_amount_rect_PolaroidProcessorDefaultsSet() - { - this.operations.Polaroid(this.rect); - var processor = this.Verify>(this.rect); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/SaturateTest.cs b/tests/ImageSharp.Tests/Processing/Filters/SaturateTest.cs deleted file mode 100644 index 4b8e80881c..0000000000 --- a/tests/ImageSharp.Tests/Processing/Filters/SaturateTest.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors.Filters; -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Processing.Filters -{ - public class SaturateTest : BaseImageOperationsExtensionTest - { - [Fact] - public void Saturation_amount_SaturationProcessorDefaultsSet() - { - this.operations.Saturate(34); - SaturateProcessor processor = this.Verify>(); - - Assert.Equal(34, processor.Amount); - } - - [Fact] - public void Saturation_amount_rect_SaturationProcessorDefaultsSet() - { - this.operations.Saturate(5, this.rect); - SaturateProcessor processor = this.Verify>(this.rect); - - Assert.Equal(5, processor.Amount); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/SepiaTest.cs b/tests/ImageSharp.Tests/Processing/Filters/SepiaTest.cs deleted file mode 100644 index 9351c8443f..0000000000 --- a/tests/ImageSharp.Tests/Processing/Filters/SepiaTest.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors.Filters; -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Processing.Filters -{ - public class SepiaTest : BaseImageOperationsExtensionTest - { - [Fact] - public void Sepia_amount_SepiaProcessorDefaultsSet() - { - this.operations.Sepia(); - var processor = this.Verify>(); - } - - [Fact] - public void Sepia_amount_rect_SepiaProcessorDefaultsSet() - { - this.operations.Sepia(this.rect); - var processor = this.Verify>(this.rect); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs index 82d7682558..eca1a458b1 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs @@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms IResampler sampler = KnownResamplers.NearestNeighbor; this.operations.Pad(width, height); - ResizeProcessor resizeProcessor = this.Verify>(); + ResizeProcessorImplementation resizeProcessor = this.Verify>(); Assert.Equal(width, resizeProcessor.Width); Assert.Equal(height, resizeProcessor.Height); diff --git a/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs index 948c79d8dd..01c43a0a45 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs @@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms int width = 50; int height = 100; this.operations.Resize(width, height); - ResizeProcessor resizeProcessor = this.Verify>(); + ResizeProcessorImplementation resizeProcessor = this.Verify>(); Assert.Equal(width, resizeProcessor.Width); Assert.Equal(height, resizeProcessor.Height); @@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms int height = 100; IResampler sampler = KnownResamplers.Lanczos3; this.operations.Resize(width, height, sampler); - ResizeProcessor resizeProcessor = this.Verify>(); + ResizeProcessorImplementation resizeProcessor = this.Verify>(); Assert.Equal(width, resizeProcessor.Width); Assert.Equal(height, resizeProcessor.Height); @@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms // ReSharper disable once ConditionIsAlwaysTrueOrFalse this.operations.Resize(width, height, sampler, compand); - ResizeProcessor resizeProcessor = this.Verify>(); + ResizeProcessorImplementation resizeProcessor = this.Verify>(); Assert.Equal(width, resizeProcessor.Width); Assert.Equal(height, resizeProcessor.Height); @@ -73,7 +73,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms }; this.operations.Resize(resizeOptions); - ResizeProcessor resizeProcessor = this.Verify>(); + ResizeProcessorImplementation resizeProcessor = this.Verify>(); Assert.Equal(width, resizeProcessor.Width); Assert.Equal(height, resizeProcessor.Height); From 475cf843bbfe04cfbd54db3cadd9d80f98ff9a3d Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 4 May 2019 01:21:06 +0200 Subject: [PATCH 108/223] fix processor invocation tests --- .../BaseImageOperationsExtensionTest.cs | 16 ++++++++++++-- .../FakeImageOperationsProvider.cs | 21 ++++++++++++++----- tests/ImageSharp.Tests/ImageOperationTests.cs | 14 ++++++------- .../Processing/Filters/FilterTest.cs | 4 ++-- .../Processing/Transforms/ResizeTests.cs | 8 +++---- 5 files changed, 43 insertions(+), 20 deletions(-) diff --git a/tests/ImageSharp.Tests/BaseImageOperationsExtensionTest.cs b/tests/ImageSharp.Tests/BaseImageOperationsExtensionTest.cs index 7adbefb346..857c19d87f 100644 --- a/tests/ImageSharp.Tests/BaseImageOperationsExtensionTest.cs +++ b/tests/ImageSharp.Tests/BaseImageOperationsExtensionTest.cs @@ -3,6 +3,7 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; using SixLabors.Primitives; using Xunit; @@ -33,7 +34,12 @@ namespace SixLabors.ImageSharp.Tests FakeImageOperationsProvider.FakeImageOperations.AppliedOperation operation = this.internalOperations.Applied[index]; - return Assert.IsType(operation.Processor); + if (operation.NonGenericProcessor != null) + { + return Assert.IsType(operation.NonGenericProcessor); + } + + return Assert.IsType(operation.GenericProcessor); } public T Verify(Rectangle rect, int index = 0) @@ -43,7 +49,13 @@ namespace SixLabors.ImageSharp.Tests FakeImageOperationsProvider.FakeImageOperations.AppliedOperation operation = this.internalOperations.Applied[index]; Assert.Equal(rect, operation.Rectangle); - return Assert.IsType(operation.Processor); + + if (operation.NonGenericProcessor != null) + { + return Assert.IsType(operation.NonGenericProcessor); + } + + return Assert.IsType(operation.GenericProcessor); } } } diff --git a/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs b/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs index 276cc5da85..c91cb16d46 100644 --- a/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs +++ b/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs @@ -69,19 +69,28 @@ namespace SixLabors.ImageSharp.Tests public IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle) { - throw new System.NotImplementedException(); + this.Applied.Add(new AppliedOperation() + { + Rectangle = rectangle, + NonGenericProcessor = processor + }); + return this; } public IImageProcessingContext ApplyProcessor(IImageProcessor processor) { - throw new System.NotImplementedException(); + this.Applied.Add(new AppliedOperation() + { + NonGenericProcessor = processor + }); + return this; } public IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle) { this.Applied.Add(new AppliedOperation { - Processor = processor, + GenericProcessor = processor, Rectangle = rectangle }); return this; @@ -91,7 +100,7 @@ namespace SixLabors.ImageSharp.Tests { this.Applied.Add(new AppliedOperation { - Processor = processor + GenericProcessor = processor }); return this; } @@ -99,7 +108,9 @@ namespace SixLabors.ImageSharp.Tests public struct AppliedOperation { public Rectangle? Rectangle { get; set; } - public IImageProcessor Processor { get; set; } + public IImageProcessor GenericProcessor { get; set; } + + public IImageProcessor NonGenericProcessor { get; set; } } } } diff --git a/tests/ImageSharp.Tests/ImageOperationTests.cs b/tests/ImageSharp.Tests/ImageOperationTests.cs index 869882f672..e694f0b644 100644 --- a/tests/ImageSharp.Tests/ImageOperationTests.cs +++ b/tests/ImageSharp.Tests/ImageOperationTests.cs @@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Tests this.image.Mutate(x => x.ApplyProcessor(this.processor)); Assert.True(this.provider.HasCreated(this.image)); - Assert.Contains(this.processor, this.provider.AppliedOperations(this.image).Select(x => x.Processor)); + Assert.Contains(this.processor, this.provider.AppliedOperations(this.image).Select(x => x.GenericProcessor)); } [Fact] @@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.Tests this.image.Mutate(this.processor); Assert.True(this.provider.HasCreated(this.image)); - Assert.Contains(this.processor, this.provider.AppliedOperations(this.image).Select(x => x.Processor)); + Assert.Contains(this.processor, this.provider.AppliedOperations(this.image).Select(x => x.GenericProcessor)); } [Fact] @@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.Tests Image returned = this.image.Clone(x => x.ApplyProcessor(this.processor)); Assert.True(this.provider.HasCreated(returned)); - Assert.Contains(this.processor, this.provider.AppliedOperations(returned).Select(x => x.Processor)); + Assert.Contains(this.processor, this.provider.AppliedOperations(returned).Select(x => x.GenericProcessor)); } [Fact] @@ -68,7 +68,7 @@ namespace SixLabors.ImageSharp.Tests Image returned = this.image.Clone(this.processor); Assert.True(this.provider.HasCreated(returned)); - Assert.Contains(this.processor, this.provider.AppliedOperations(returned).Select(x => x.Processor)); + Assert.Contains(this.processor, this.provider.AppliedOperations(returned).Select(x => x.GenericProcessor)); } [Fact] @@ -76,7 +76,7 @@ namespace SixLabors.ImageSharp.Tests { Image returned = this.image.Clone(x => x.ApplyProcessor(this.processor)); Assert.False(this.provider.HasCreated(this.image)); - Assert.DoesNotContain(this.processor, this.provider.AppliedOperations(this.image).Select(x => x.Processor)); + Assert.DoesNotContain(this.processor, this.provider.AppliedOperations(this.image).Select(x => x.GenericProcessor)); } [Fact] @@ -84,7 +84,7 @@ namespace SixLabors.ImageSharp.Tests { Image returned = this.image.Clone(this.processor); Assert.False(this.provider.HasCreated(this.image)); - Assert.DoesNotContain(this.processor, this.provider.AppliedOperations(this.image).Select(x => x.Processor)); + Assert.DoesNotContain(this.processor, this.provider.AppliedOperations(this.image).Select(x => x.GenericProcessor)); } [Fact] @@ -92,7 +92,7 @@ namespace SixLabors.ImageSharp.Tests { var operations = new FakeImageOperationsProvider.FakeImageOperations(null, false); operations.ApplyProcessors(this.processor); - Assert.Contains(this.processor, operations.Applied.Select(x => x.Processor)); + Assert.Contains(this.processor, operations.Applied.Select(x => x.GenericProcessor)); } public void Dispose() => this.image.Dispose(); diff --git a/tests/ImageSharp.Tests/Processing/Filters/FilterTest.cs b/tests/ImageSharp.Tests/Processing/Filters/FilterTest.cs index f4efb12748..d598f0ac88 100644 --- a/tests/ImageSharp.Tests/Processing/Filters/FilterTest.cs +++ b/tests/ImageSharp.Tests/Processing/Filters/FilterTest.cs @@ -17,14 +17,14 @@ namespace SixLabors.ImageSharp.Tests.Processing.Filters public void Filter_CorrectProcessor() { this.operations.Filter(KnownFilterMatrices.AchromatomalyFilter * KnownFilterMatrices.CreateHueFilter(90F)); - FilterProcessorImplementation p = this.Verify>(); + FilterProcessor p = this.Verify(); } [Fact] public void Filter_rect_CorrectProcessor() { this.operations.Filter(KnownFilterMatrices.AchromatomalyFilter * KnownFilterMatrices.CreateHueFilter(90F), this.rect); - FilterProcessorImplementation p = this.Verify>(this.rect); + FilterProcessor p = this.Verify(this.rect); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs index 01c43a0a45..570ac4e2cd 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs @@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms int width = 50; int height = 100; this.operations.Resize(width, height); - ResizeProcessorImplementation resizeProcessor = this.Verify>(); + ResizeProcessor resizeProcessor = this.Verify(); Assert.Equal(width, resizeProcessor.Width); Assert.Equal(height, resizeProcessor.Height); @@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms int height = 100; IResampler sampler = KnownResamplers.Lanczos3; this.operations.Resize(width, height, sampler); - ResizeProcessorImplementation resizeProcessor = this.Verify>(); + ResizeProcessor resizeProcessor = this.Verify(); Assert.Equal(width, resizeProcessor.Width); Assert.Equal(height, resizeProcessor.Height); @@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms // ReSharper disable once ConditionIsAlwaysTrueOrFalse this.operations.Resize(width, height, sampler, compand); - ResizeProcessorImplementation resizeProcessor = this.Verify>(); + ResizeProcessor resizeProcessor = this.Verify(); Assert.Equal(width, resizeProcessor.Width); Assert.Equal(height, resizeProcessor.Height); @@ -73,7 +73,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms }; this.operations.Resize(resizeOptions); - ResizeProcessorImplementation resizeProcessor = this.Verify>(); + ResizeProcessor resizeProcessor = this.Verify(); Assert.Equal(width, resizeProcessor.Width); Assert.Equal(height, resizeProcessor.Height); From 134d4845e237d1864a56b0b77c13ecc1a66e5a3f Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 4 May 2019 01:26:10 +0200 Subject: [PATCH 109/223] re-add and fix filter invocation tests --- .../Processing/Filters/BlackWhiteTest.cs | 27 +++++++++++ .../Processing/Filters/BrightnessTest.cs | 31 ++++++++++++ .../Processing/Filters/ColorBlindnessTest.cs | 46 ++++++++++++++++++ .../Processing/Filters/ContrastTest.cs | 33 +++++++++++++ .../Processing/Filters/GrayscaleTest.cs | 47 +++++++++++++++++++ .../Processing/Filters/HueTest.cs | 32 +++++++++++++ .../Processing/Filters/InvertTest.cs | 27 +++++++++++ .../Processing/Filters/KodachromeTest.cs | 27 +++++++++++ .../Processing/Filters/LomographTest.cs | 31 ++++++++++++ .../Processing/Filters/OpacityTest.cs | 31 ++++++++++++ .../Processing/Filters/PolaroidTest.cs | 28 +++++++++++ .../Processing/Filters/SaturateTest.cs | 31 ++++++++++++ .../Processing/Filters/SepiaTest.cs | 27 +++++++++++ 13 files changed, 418 insertions(+) create mode 100644 tests/ImageSharp.Tests/Processing/Filters/BlackWhiteTest.cs create mode 100644 tests/ImageSharp.Tests/Processing/Filters/BrightnessTest.cs create mode 100644 tests/ImageSharp.Tests/Processing/Filters/ColorBlindnessTest.cs create mode 100644 tests/ImageSharp.Tests/Processing/Filters/ContrastTest.cs create mode 100644 tests/ImageSharp.Tests/Processing/Filters/GrayscaleTest.cs create mode 100644 tests/ImageSharp.Tests/Processing/Filters/HueTest.cs create mode 100644 tests/ImageSharp.Tests/Processing/Filters/InvertTest.cs create mode 100644 tests/ImageSharp.Tests/Processing/Filters/KodachromeTest.cs create mode 100644 tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs create mode 100644 tests/ImageSharp.Tests/Processing/Filters/OpacityTest.cs create mode 100644 tests/ImageSharp.Tests/Processing/Filters/PolaroidTest.cs create mode 100644 tests/ImageSharp.Tests/Processing/Filters/SaturateTest.cs create mode 100644 tests/ImageSharp.Tests/Processing/Filters/SepiaTest.cs diff --git a/tests/ImageSharp.Tests/Processing/Filters/BlackWhiteTest.cs b/tests/ImageSharp.Tests/Processing/Filters/BlackWhiteTest.cs new file mode 100644 index 0000000000..f913068e35 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Filters/BlackWhiteTest.cs @@ -0,0 +1,27 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Filters; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Filters +{ + public class BlackWhiteTest : BaseImageOperationsExtensionTest + { + [Fact] + public void BlackWhite_CorrectProcessor() + { + this.operations.BlackWhite(); + BlackWhiteProcessor p = this.Verify(); + } + + [Fact] + public void BlackWhite_rect_CorrectProcessor() + { + this.operations.BlackWhite(this.rect); + BlackWhiteProcessor p = this.Verify(this.rect); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/BrightnessTest.cs b/tests/ImageSharp.Tests/Processing/Filters/BrightnessTest.cs new file mode 100644 index 0000000000..c26524880a --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Filters/BrightnessTest.cs @@ -0,0 +1,31 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Filters; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Effects +{ + public class BrightnessTest : BaseImageOperationsExtensionTest + { + [Fact] + public void Brightness_amount_BrightnessProcessorDefaultsSet() + { + this.operations.Brightness(1.5F); + BrightnessProcessor processor = this.Verify(); + + Assert.Equal(1.5F, processor.Amount); + } + + [Fact] + public void Brightness_amount_rect_BrightnessProcessorDefaultsSet() + { + this.operations.Brightness(1.5F, this.rect); + BrightnessProcessor processor = this.Verify(this.rect); + + Assert.Equal(1.5F, processor.Amount); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/ColorBlindnessTest.cs b/tests/ImageSharp.Tests/Processing/Filters/ColorBlindnessTest.cs new file mode 100644 index 0000000000..7dd894403d --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Filters/ColorBlindnessTest.cs @@ -0,0 +1,46 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Collections.Generic; + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Filters; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.ImageSharp.Tests.TestUtilities; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Filters +{ + public class ColorBlindnessTest : BaseImageOperationsExtensionTest + { + public static IEnumerable TheoryData = new[] { + new object[]{ new TestType(), ColorBlindnessMode.Achromatomaly }, + new object[]{ new TestType(), ColorBlindnessMode.Achromatopsia }, + new object[]{ new TestType(), ColorBlindnessMode.Deuteranomaly }, + new object[]{ new TestType(), ColorBlindnessMode.Deuteranopia }, + new object[]{ new TestType(), ColorBlindnessMode.Protanomaly }, + new object[]{ new TestType(), ColorBlindnessMode.Protanopia }, + new object[]{ new TestType(), ColorBlindnessMode.Tritanomaly }, + new object[]{ new TestType(), ColorBlindnessMode.Tritanopia } + }; + + [Theory] + [MemberData(nameof(TheoryData))] + public void ColorBlindness_CorrectProcessor(TestType testType, ColorBlindnessMode colorBlindness) + where T : IImageProcessor + { + this.operations.ColorBlindness(colorBlindness); + T p = this.Verify(); + } + [Theory] + [MemberData(nameof(TheoryData))] + public void ColorBlindness_rect_CorrectProcessor(TestType testType, ColorBlindnessMode colorBlindness) + where T : IImageProcessor + { + this.operations.ColorBlindness(colorBlindness, this.rect); + T p = this.Verify(this.rect); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/ContrastTest.cs b/tests/ImageSharp.Tests/Processing/Filters/ContrastTest.cs new file mode 100644 index 0000000000..ff2b1c702d --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Filters/ContrastTest.cs @@ -0,0 +1,33 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Effects +{ + using SixLabors.ImageSharp.Processing; + using SixLabors.ImageSharp.Processing.Processors.Filters; + + public class ContrastTest : BaseImageOperationsExtensionTest + { + [Fact] + public void Contrast_amount_ContrastProcessorDefaultsSet() + { + this.operations.Contrast(1.5F); + ContrastProcessor processor = this.Verify(); + + Assert.Equal(1.5F, processor.Amount); + } + + [Fact] + public void Contrast_amount_rect_ContrastProcessorDefaultsSet() + { + this.operations.Contrast(1.5F, this.rect); + ContrastProcessor processor = this.Verify(this.rect); + + Assert.Equal(1.5F, processor.Amount); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/GrayscaleTest.cs b/tests/ImageSharp.Tests/Processing/Filters/GrayscaleTest.cs new file mode 100644 index 0000000000..94e1b00a71 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Filters/GrayscaleTest.cs @@ -0,0 +1,47 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Collections.Generic; + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Filters; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.ImageSharp.Tests.TestUtilities; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Filters +{ + public class GrayscaleTest : BaseImageOperationsExtensionTest + { + public static IEnumerable ModeTheoryData = new[] { + new object[]{ new TestType(), GrayscaleMode.Bt709 } + }; + + [Theory] + [MemberData(nameof(ModeTheoryData))] + public void Grayscale_mode_CorrectProcessor(TestType testType, GrayscaleMode mode) + where T : IImageProcessor + { + this.operations.Grayscale(mode); + var p = this.Verify(); + } + + [Theory] + [MemberData(nameof(ModeTheoryData))] + public void Grayscale_mode_rect_CorrectProcessor(TestType testType, GrayscaleMode mode) + where T : IImageProcessor + { + this.operations.Grayscale(mode, this.rect); + this.Verify(this.rect); + } + + [Fact] + public void Grayscale_rect_CorrectProcessor() + { + this.operations.Grayscale(this.rect); + this.Verify(this.rect); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/HueTest.cs b/tests/ImageSharp.Tests/Processing/Filters/HueTest.cs new file mode 100644 index 0000000000..0eb25043c7 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Filters/HueTest.cs @@ -0,0 +1,32 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Filters; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Filters +{ + public class HueTest : BaseImageOperationsExtensionTest + { + [Fact] + public void Hue_amount_HueProcessorDefaultsSet() + { + this.operations.Hue(34f); + var processor = this.Verify(); + + Assert.Equal(34f, processor.Degrees); + } + + [Fact] + public void Hue_amount_rect_HueProcessorDefaultsSet() + { + this.operations.Hue(5f, this.rect); + var processor = this.Verify(this.rect); + + Assert.Equal(5f, processor.Degrees); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/InvertTest.cs b/tests/ImageSharp.Tests/Processing/Filters/InvertTest.cs new file mode 100644 index 0000000000..ff59eab054 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Filters/InvertTest.cs @@ -0,0 +1,27 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Filters; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Effects +{ + public class InvertTest : BaseImageOperationsExtensionTest + { + [Fact] + public void Invert_InvertProcessorDefaultsSet() + { + this.operations.Invert(); + var processor = this.Verify(); + } + + [Fact] + public void Pixelate_rect_PixelateProcessorDefaultsSet() + { + this.operations.Invert(this.rect); + var processor = this.Verify(this.rect); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/KodachromeTest.cs b/tests/ImageSharp.Tests/Processing/Filters/KodachromeTest.cs new file mode 100644 index 0000000000..a06a9d65c0 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Filters/KodachromeTest.cs @@ -0,0 +1,27 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Filters; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Filters +{ + public class KodachromeTest : BaseImageOperationsExtensionTest + { + [Fact] + public void Kodachrome_amount_KodachromeProcessorDefaultsSet() + { + this.operations.Kodachrome(); + var processor = this.Verify(); + } + + [Fact] + public void Kodachrome_amount_rect_KodachromeProcessorDefaultsSet() + { + this.operations.Kodachrome(this.rect); + var processor = this.Verify(this.rect); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs b/tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs new file mode 100644 index 0000000000..446cfa1c33 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs @@ -0,0 +1,31 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.IO; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests +{ + using SixLabors.ImageSharp.Processing; + using SixLabors.ImageSharp.Processing.Processors.Filters; + + public class LomographTest : BaseImageOperationsExtensionTest + { + [Fact] + public void Lomograph_amount_LomographProcessorDefaultsSet() + { + this.operations.Lomograph(); + var processor = this.Verify(); + } + + [Fact] + public void Lomograph_amount_rect_LomographProcessorDefaultsSet() + { + this.operations.Lomograph(this.rect); + var processor = this.Verify(this.rect); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/OpacityTest.cs b/tests/ImageSharp.Tests/Processing/Filters/OpacityTest.cs new file mode 100644 index 0000000000..95ea2c23e9 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Filters/OpacityTest.cs @@ -0,0 +1,31 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Filters; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Effects +{ + public class OpacityTest : BaseImageOperationsExtensionTest + { + [Fact] + public void Alpha_amount_AlphaProcessorDefaultsSet() + { + this.operations.Opacity(0.2f); + OpacityProcessor processor = this.Verify(); + + Assert.Equal(.2f, processor.Amount); + } + + [Fact] + public void Alpha_amount_rect_AlphaProcessorDefaultsSet() + { + this.operations.Opacity(0.6f, this.rect); + OpacityProcessor processor = this.Verify(this.rect); + + Assert.Equal(.6f, processor.Amount); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/PolaroidTest.cs b/tests/ImageSharp.Tests/Processing/Filters/PolaroidTest.cs new file mode 100644 index 0000000000..f63b892bc9 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Filters/PolaroidTest.cs @@ -0,0 +1,28 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Filters; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Filters +{ + public class PolaroidTest : BaseImageOperationsExtensionTest + { + [Fact] + public void Polaroid_amount_PolaroidProcessorDefaultsSet() + { + this.operations.Polaroid(); + var processor = this.Verify(); + } + + [Fact] + public void Polaroid_amount_rect_PolaroidProcessorDefaultsSet() + { + this.operations.Polaroid(this.rect); + var processor = this.Verify(this.rect); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/SaturateTest.cs b/tests/ImageSharp.Tests/Processing/Filters/SaturateTest.cs new file mode 100644 index 0000000000..b1583b4aca --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Filters/SaturateTest.cs @@ -0,0 +1,31 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Filters; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Filters +{ + public class SaturateTest : BaseImageOperationsExtensionTest + { + [Fact] + public void Saturation_amount_SaturationProcessorDefaultsSet() + { + this.operations.Saturate(34); + SaturateProcessor processor = this.Verify(); + + Assert.Equal(34, processor.Amount); + } + + [Fact] + public void Saturation_amount_rect_SaturationProcessorDefaultsSet() + { + this.operations.Saturate(5, this.rect); + SaturateProcessor processor = this.Verify(this.rect); + + Assert.Equal(5, processor.Amount); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/SepiaTest.cs b/tests/ImageSharp.Tests/Processing/Filters/SepiaTest.cs new file mode 100644 index 0000000000..441c9739d7 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Filters/SepiaTest.cs @@ -0,0 +1,27 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Filters; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Filters +{ + public class SepiaTest : BaseImageOperationsExtensionTest + { + [Fact] + public void Sepia_amount_SepiaProcessorDefaultsSet() + { + this.operations.Sepia(); + var processor = this.Verify(); + } + + [Fact] + public void Sepia_amount_rect_SepiaProcessorDefaultsSet() + { + this.operations.Sepia(this.rect); + var processor = this.Verify(this.rect); + } + } +} \ No newline at end of file From d8b918f14861d9d459599c9f0eadd30864e2835a Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 4 May 2019 02:17:33 +0200 Subject: [PATCH 110/223] fix warnings and improve xmldocs --- .../Advanced/AdvancedImageExtensions.cs | 1 - src/ImageSharp/Formats/Bmp/BmpDecoder.cs | 1 + src/ImageSharp/Formats/Gif/GifDecoder.cs | 3 +- src/ImageSharp/Formats/IImageDecoder.cs | 11 ++- src/ImageSharp/Formats/Jpeg/JpegDecoder.cs | 3 +- src/ImageSharp/Formats/Png/PngDecoder.cs | 3 +- src/ImageSharp/IImageVisitor.cs | 22 +++++ src/ImageSharp/Image.Decode.cs | 4 +- src/ImageSharp/Image.FromFile.cs | 13 ++- src/ImageSharp/Image.FromStream.cs | 1 + src/ImageSharp/Image.WrapMemory.cs | 8 +- src/ImageSharp/Image.cs | 43 ++++++---- src/ImageSharp/ImageExtensions.cs | 3 - src/ImageSharp/Image{TPixel}.cs | 25 ++---- .../Processing/IImageProcessingContext.cs | 42 +++++++++ .../IImageProcessingContext{TPixel}.cs | 26 +----- src/ImageSharp/Processing/PadExtensions.cs | 3 +- .../Processing/PolaroidExtensions.cs | 1 - .../Processing/ProcessingExtensions.cs | 85 +++++++++++-------- .../Processors/Filters/BlackWhiteProcessor.cs | 3 +- .../Processors/Filters/FilterProcessor.cs | 4 + .../LomographProcessorImplementation.cs | 13 +-- .../Processors/Filters/OpacityProcessor.cs | 3 +- .../Processors/Filters/PolaroidProcessor.cs | 26 +----- .../PolaroidProcessorImplementation.cs | 31 +++++++ .../Processors/Filters/ProtanopiaProcessor.cs | 1 - .../Processing/Processors/IImageProcessor.cs | 13 ++- .../Transforms/Resize/ResizeProcessor.cs | 72 +++++++++------- .../Resize/ResizeProcessorImplementation.cs | 6 +- src/ImageSharp/Processing/ResizeExtensions.cs | 10 --- .../ImageSharp.Benchmarks.csproj | 3 +- 31 files changed, 287 insertions(+), 196 deletions(-) create mode 100644 src/ImageSharp/IImageVisitor.cs create mode 100644 src/ImageSharp/Processing/IImageProcessingContext.cs create mode 100644 src/ImageSharp/Processing/Processors/Filters/PolaroidProcessorImplementation.cs diff --git a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs index 378b6e8b96..e0d7a4b184 100644 --- a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs +++ b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs @@ -18,7 +18,6 @@ namespace SixLabors.ImageSharp.Advanced /// /// Gets the configuration for the image. /// - /// The Pixel format. /// The source image. /// Returns the configuration. public static Configuration GetConfiguration(this Image source) diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs index 4922a92003..ebb7ffdf3c 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs @@ -30,6 +30,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp return new BmpDecoderCore(configuration, this).Decode(stream); } + /// public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); /// diff --git a/src/ImageSharp/Formats/Gif/GifDecoder.cs b/src/ImageSharp/Formats/Gif/GifDecoder.cs index 6498f4aa27..1addcd0abf 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoder.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoder.cs @@ -44,7 +44,8 @@ namespace SixLabors.ImageSharp.Formats.Gif var decoder = new GifDecoderCore(configuration, this); return decoder.Identify(stream); } - + + /// public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); } } diff --git a/src/ImageSharp/Formats/IImageDecoder.cs b/src/ImageSharp/Formats/IImageDecoder.cs index 625c4efb6f..8dafdac795 100644 --- a/src/ImageSharp/Formats/IImageDecoder.cs +++ b/src/ImageSharp/Formats/IImageDecoder.cs @@ -12,15 +12,22 @@ namespace SixLabors.ImageSharp.Formats public interface IImageDecoder { /// - /// Decodes the image from the specified stream to the . + /// Decodes the image from the specified stream to an of a specific pixel type. /// /// The pixel format. /// The configuration for the image. /// The containing image data. - /// The decoded image + /// The decoded image of a given pixel type. Image Decode(Configuration configuration, Stream stream) where TPixel : struct, IPixel; + /// + /// Decodes the image from the specified stream to an . + /// The decoder is free to choose the pixel type. + /// + /// The configuration for the image. + /// The containing image data. + /// The decoded image of a pixel type chosen by the decoder. Image Decode(Configuration configuration, Stream stream); } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs index 7459abec56..a1bf048521 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs @@ -38,7 +38,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg return decoder.Identify(stream); } } - + + /// public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Png/PngDecoder.cs b/src/ImageSharp/Formats/Png/PngDecoder.cs index e8c5ac8e81..040da94737 100644 --- a/src/ImageSharp/Formats/Png/PngDecoder.cs +++ b/src/ImageSharp/Formats/Png/PngDecoder.cs @@ -59,7 +59,8 @@ namespace SixLabors.ImageSharp.Formats.Png var decoder = new PngDecoderCore(configuration, this); return decoder.Identify(stream); } - + + /// public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); } } \ No newline at end of file diff --git a/src/ImageSharp/IImageVisitor.cs b/src/ImageSharp/IImageVisitor.cs new file mode 100644 index 0000000000..971c4d37cb --- /dev/null +++ b/src/ImageSharp/IImageVisitor.cs @@ -0,0 +1,22 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp +{ + /// + /// A visitor to implement double-dispatch pattern in order to apply pixel-specific operations + /// on non-generic instances. The operation is dispatched by . + /// + internal interface IImageVisitor + { + /// + /// Provides a pixel-specific implementation for a given operation. + /// + /// The image. + /// The pixel type. + void Visit(Image image) + where TPixel : struct, IPixel; + } +} \ No newline at end of file diff --git a/src/ImageSharp/Image.Decode.cs b/src/ImageSharp/Image.Decode.cs index 17a15a9160..8d0df599ea 100644 --- a/src/ImageSharp/Image.Decode.cs +++ b/src/ImageSharp/Image.Decode.cs @@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp /// The image might be filled with memory garbage. /// /// The pixel type - /// The + /// The /// The width of the image /// The height of the image /// The @@ -102,7 +102,7 @@ namespace SixLabors.ImageSharp Image img = decoder.Decode(config, stream); return (img, format); } - + private static (Image img, IImageFormat format) Decode(Stream stream, Configuration config) { IImageDecoder decoder = DiscoverDecoder(stream, config, out IImageFormat format); diff --git a/src/ImageSharp/Image.FromFile.cs b/src/ImageSharp/Image.FromFile.cs index 0b49668862..61500e20d7 100644 --- a/src/ImageSharp/Image.FromFile.cs +++ b/src/ImageSharp/Image.FromFile.cs @@ -162,7 +162,18 @@ namespace SixLabors.ImageSharp return Load(config, stream, out format); } } - + + /// + /// Create a new instance of the class from the given file. + /// The pixel type is selected by the decoder. + /// + /// The configuration options. + /// The file path to the image. + /// The mime type of the decoded image. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// A new . public static Image Load(Configuration config, string path, out IImageFormat format) { using (Stream stream = config.FileSystem.OpenRead(path)) diff --git a/src/ImageSharp/Image.FromStream.cs b/src/ImageSharp/Image.FromStream.cs index b16501b0f2..a4a9eeec75 100644 --- a/src/ImageSharp/Image.FromStream.cs +++ b/src/ImageSharp/Image.FromStream.cs @@ -182,6 +182,7 @@ namespace SixLabors.ImageSharp throw new NotSupportedException(sb.ToString()); } + private static Image Load(Configuration config, Stream stream, out IImageFormat format) { config = config ?? Configuration.Default; diff --git a/src/ImageSharp/Image.WrapMemory.cs b/src/ImageSharp/Image.WrapMemory.cs index 49bc99e9ee..095991b076 100644 --- a/src/ImageSharp/Image.WrapMemory.cs +++ b/src/ImageSharp/Image.WrapMemory.cs @@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp /// allowing to view/manipulate it as an ImageSharp instance. /// /// The pixel type - /// The + /// The /// The pixel memory. /// The width of the memory image. /// The height of the memory image. @@ -43,7 +43,7 @@ namespace SixLabors.ImageSharp /// allowing to view/manipulate it as an ImageSharp instance. /// /// The pixel type - /// The + /// The /// The pixel memory. /// The width of the memory image. /// The height of the memory image. @@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp /// It will be disposed together with the result image. /// /// The pixel type - /// The + /// The /// The that is being transferred to the image /// The width of the memory image. /// The height of the memory image. @@ -111,7 +111,7 @@ namespace SixLabors.ImageSharp /// It will be disposed together with the result image. /// /// The pixel type. - /// The + /// The /// The that is being transferred to the image. /// The width of the memory image. /// The height of the memory image. diff --git a/src/ImageSharp/Image.cs b/src/ImageSharp/Image.cs index a27f16ab30..d0a1c4319d 100644 --- a/src/ImageSharp/Image.cs +++ b/src/ImageSharp/Image.cs @@ -10,15 +10,30 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp { - internal interface IImageVisitor - { - void Visit(Image image) - where TPixel : struct, IPixel; - } - + /// + /// Encapsulates an image, which consists of the pixel data for a graphics image and its attributes. + /// For the non-generic type, the pixel type is only known at runtime. + /// is always implemented by a pixel-specific instance. + /// public abstract partial class Image : IImage, IConfigurable { - protected readonly Configuration configuration; + /// + /// Initializes a new instance of the class. + /// + /// The . + /// The . + /// The . + protected Image(Configuration configuration, PixelTypeInfo pixelType, ImageMetadata metadata) + { + this.Configuration = configuration ?? Configuration.Default; + this.PixelType = pixelType; + this.Metadata = metadata ?? new ImageMetadata(); + } + + /// + /// Gets the . + /// + protected Configuration Configuration { get; } /// public PixelTypeInfo PixelType { get; } @@ -28,22 +43,16 @@ namespace SixLabors.ImageSharp /// public abstract int Height { get; } - + /// public ImageMetadata Metadata { get; } /// /// Gets the pixel buffer. /// - Configuration IConfigurable.Configuration => this.configuration; - - protected Image(Configuration configuration, PixelTypeInfo pixelType, ImageMetadata metadata) - { - this.configuration = configuration ?? Configuration.Default; - this.PixelType = pixelType; - this.Metadata = metadata ?? new ImageMetadata(); - } + Configuration IConfigurable.Configuration => this.Configuration; + /// public abstract void Dispose(); internal abstract void AcceptVisitor(IImageVisitor visitor); @@ -63,7 +72,7 @@ namespace SixLabors.ImageSharp this.AcceptVisitor(visitor); } - class EncodeVisitor : IImageVisitor + private class EncodeVisitor : IImageVisitor { private readonly IImageEncoder encoder; diff --git a/src/ImageSharp/ImageExtensions.cs b/src/ImageSharp/ImageExtensions.cs index 3bd60694d5..ec4c364d83 100644 --- a/src/ImageSharp/ImageExtensions.cs +++ b/src/ImageSharp/ImageExtensions.cs @@ -19,7 +19,6 @@ namespace SixLabors.ImageSharp /// /// Writes the image to the given stream using the currently loaded image format. /// - /// The pixel format. /// The source image. /// The file path to save the image to. /// Thrown if the stream is null. @@ -61,7 +60,6 @@ namespace SixLabors.ImageSharp /// /// Writes the image to the given stream using the currently loaded image format. /// - /// The pixel format. /// The source image. /// The file path to save the image to. /// The encoder to save the image with. @@ -78,7 +76,6 @@ namespace SixLabors.ImageSharp /// /// Writes the image to the given stream using the currently loaded image format. /// - /// The Pixel format. /// The source image. /// The stream to save the image to. /// The format to save the image in. diff --git a/src/ImageSharp/Image{TPixel}.cs b/src/ImageSharp/Image{TPixel}.cs index ea6d0beb25..243e2912be 100644 --- a/src/ImageSharp/Image{TPixel}.cs +++ b/src/ImageSharp/Image{TPixel}.cs @@ -16,6 +16,7 @@ namespace SixLabors.ImageSharp { /// /// Encapsulates an image, which consists of the pixel data for a graphics image and its attributes. + /// For generic -s the pixel type is known at compile time. /// /// The pixel format. public sealed class Image : Image @@ -68,8 +69,6 @@ namespace SixLabors.ImageSharp internal Image(Configuration configuration, int width, int height, ImageMetadata metadata) : base(configuration, PixelTypeInfo.Create(), metadata) { - this.PixelType = new PixelTypeInfo(Unsafe.SizeOf() * 8); - this.Metadata = metadata ?? new ImageMetadata(); this.Frames = new ImageFrameCollection(this, width, height, default(TPixel)); } @@ -85,8 +84,6 @@ namespace SixLabors.ImageSharp internal Image(Configuration configuration, MemorySource memorySource, int width, int height, ImageMetadata metadata) : base(configuration, PixelTypeInfo.Create(), metadata) { - this.PixelType = new PixelTypeInfo(Unsafe.SizeOf() * 8); - this.Metadata = metadata; this.Frames = new ImageFrameCollection(this, width, height, memorySource); } @@ -102,8 +99,6 @@ namespace SixLabors.ImageSharp internal Image(Configuration configuration, int width, int height, TPixel backgroundColor, ImageMetadata metadata) : base(configuration, PixelTypeInfo.Create(), metadata) { - this.PixelType = new PixelTypeInfo(Unsafe.SizeOf() * 8); - this.Metadata = metadata ?? new ImageMetadata(); this.Frames = new ImageFrameCollection(this, width, height, backgroundColor); } @@ -115,27 +110,18 @@ namespace SixLabors.ImageSharp /// The images metadata. /// The frames that will be owned by this image instance. internal Image(Configuration configuration, ImageMetadata metadata, IEnumerable> frames) - : base(configuration,PixelTypeInfo.Create(), metadata) + : base(configuration, PixelTypeInfo.Create(), metadata) { - this.PixelType = new PixelTypeInfo(Unsafe.SizeOf() * 8); - this.Metadata = metadata ?? new ImageMetadata(); - this.Frames = new ImageFrameCollection(this, frames); } - - /// - public PixelTypeInfo PixelType { get; } - /// public override int Width => this.Frames.RootFrame.Width; /// public override int Height => this.Frames.RootFrame.Height; - /// - public ImageMetadata Metadata { get; } - + /// /// Gets the frames. /// public ImageFrameCollection Frames { get; } @@ -154,7 +140,6 @@ namespace SixLabors.ImageSharp public TPixel this[int x, int y] { get => this.PixelSource.PixelBuffer[x, y]; - set => this.PixelSource.PixelBuffer[x, y] = value; } @@ -162,7 +147,7 @@ namespace SixLabors.ImageSharp /// Clones the current image /// /// Returns a new image with all the same metadata as the original. - public Image Clone() => this.Clone(this.configuration); + public Image Clone() => this.Clone(this.Configuration); /// /// Clones the current image with the given configuration. @@ -181,7 +166,7 @@ namespace SixLabors.ImageSharp /// The pixel format. /// The public Image CloneAs() - where TPixel2 : struct, IPixel => this.CloneAs(this.configuration); + where TPixel2 : struct, IPixel => this.CloneAs(this.Configuration); /// /// Returns a copy of the image in the given pixel format. diff --git a/src/ImageSharp/Processing/IImageProcessingContext.cs b/src/ImageSharp/Processing/IImageProcessingContext.cs new file mode 100644 index 0000000000..509b1313d9 --- /dev/null +++ b/src/ImageSharp/Processing/IImageProcessingContext.cs @@ -0,0 +1,42 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Memory; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing +{ + /// + /// A pixel-agnostic interface to queue up image operations to apply to an image. + /// + public interface IImageProcessingContext + { + /// + /// Gets a reference to the used to allocate buffers + /// for this context. + /// + MemoryAllocator MemoryAllocator { get; } + + /// + /// Gets the image dimensions at the current point in the processing pipeline. + /// + /// The . + Size GetCurrentSize(); + + /// + /// Adds the processor to the current set of image operations to be applied. + /// + /// The processor to apply. + /// The area to apply it to. + /// The current operations class to allow chaining of operations. + IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle); + + /// + /// Adds the processor to the current set of image operations to be applied. + /// + /// The processor to apply. + /// The current operations class to allow chaining of operations. + IImageProcessingContext ApplyProcessor(IImageProcessor processor); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/IImageProcessingContext{TPixel}.cs b/src/ImageSharp/Processing/IImageProcessingContext{TPixel}.cs index fefa973f7c..b49cfe2151 100644 --- a/src/ImageSharp/Processing/IImageProcessingContext{TPixel}.cs +++ b/src/ImageSharp/Processing/IImageProcessingContext{TPixel}.cs @@ -3,32 +3,12 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors; -using SixLabors.Memory; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { - public interface IImageProcessingContext - { - /// - /// Gets a reference to the used to allocate buffers - /// for this context. - /// - MemoryAllocator MemoryAllocator { get; } - - /// - /// Gets the image dimensions at the current point in the processing pipeline. - /// - /// The - Size GetCurrentSize(); - - IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle); - - IImageProcessingContext ApplyProcessor(IImageProcessor processor); - } - /// - /// An interface to queue up image operations to apply to an image. + /// A pixel-specific interface to queue up image operations to apply to an image. /// /// The pixel format public interface IImageProcessingContext : IImageProcessingContext @@ -37,8 +17,8 @@ namespace SixLabors.ImageSharp.Processing /// /// Adds the processor to the current set of image operations to be applied. /// - /// The processor to apply - /// The area to apply it to + /// The processor to apply. + /// The area to apply it to. /// The current operations class to allow chaining of operations. IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle); diff --git a/src/ImageSharp/Processing/PadExtensions.cs b/src/ImageSharp/Processing/PadExtensions.cs index 2db219795f..0422f7c59d 100644 --- a/src/ImageSharp/Processing/PadExtensions.cs +++ b/src/ImageSharp/Processing/PadExtensions.cs @@ -14,7 +14,6 @@ namespace SixLabors.ImageSharp.Processing /// /// Evenly pads an image to fit the new dimensions. /// - /// The pixel format. /// The source image to pad. /// The new width. /// The new height. @@ -25,7 +24,7 @@ namespace SixLabors.ImageSharp.Processing { Size = new Size(width, height), Mode = ResizeMode.BoxPad, - Sampler = KnownResamplers.NearestNeighbor + Sampler = KnownResamplers.NearestNeighbor, }; return source.Resize(options); diff --git a/src/ImageSharp/Processing/PolaroidExtensions.cs b/src/ImageSharp/Processing/PolaroidExtensions.cs index 4172c1d615..4e78411689 100644 --- a/src/ImageSharp/Processing/PolaroidExtensions.cs +++ b/src/ImageSharp/Processing/PolaroidExtensions.cs @@ -23,7 +23,6 @@ namespace SixLabors.ImageSharp.Processing /// /// Alters the colors of the image recreating an old Polaroid camera effect. /// - /// The pixel format. /// The image this method extends. /// /// The structure that specifies the portion of the image object to alter. diff --git a/src/ImageSharp/Processing/ProcessingExtensions.cs b/src/ImageSharp/Processing/ProcessingExtensions.cs index a1acf3aa0b..c72e8cfb77 100644 --- a/src/ImageSharp/Processing/ProcessingExtensions.cs +++ b/src/ImageSharp/Processing/ProcessingExtensions.cs @@ -13,43 +13,6 @@ namespace SixLabors.ImageSharp.Processing /// public static class ProcessingExtensions { - class ProcessingVisitor : IImageVisitor - { - private readonly Action operation; - - private readonly bool mutate; - - public Image ResultImage { get; private set; } - - public ProcessingVisitor(Action operation, bool mutate) - { - this.operation = operation; - this.mutate = mutate; - } - - public void Visit(Image image) - where TPixel : struct, IPixel - { - IInternalImageProcessingContext operationsRunner = image.GetConfiguration() - .ImageOperationsProvider.CreateImageProcessingContext(image, this.mutate); - this.operation(operationsRunner); - this.ResultImage = operationsRunner.Apply(); - } - } - - public static void Mutate(this Image source, Action operation) - { - ProcessingVisitor visitor = new ProcessingVisitor(operation, true); - source.AcceptVisitor(visitor); - } - - public static Image Clone(this Image source, Action operation) - { - ProcessingVisitor visitor = new ProcessingVisitor(operation, false); - source.AcceptVisitor(visitor); - return visitor.ResultImage; - } - /// /// Applies the given operation to the mutable image. /// Useful when we need to extract information like Width/Height to parametrize the next operation working on the chain. @@ -62,6 +25,17 @@ namespace SixLabors.ImageSharp.Processing public static IImageProcessingContext Apply(this IImageProcessingContext source, Action> operation) where TPixel : struct, IPixel => source.ApplyProcessor(new DelegateProcessor(operation)); + /// + /// Mutates the source image by applying the image operation to it. + /// + /// The image to mutate. + /// The operation to perform on the source. + public static void Mutate(this Image source, Action operation) + { + ProcessingVisitor visitor = new ProcessingVisitor(operation, true); + source.AcceptVisitor(visitor); + } + /// /// Mutates the source image by applying the image operation to it. /// @@ -96,6 +70,19 @@ namespace SixLabors.ImageSharp.Processing operationsRunner.Apply(); } + /// + /// Creates a deep clone of the current image. The clone is then mutated by the given operation. + /// + /// The image to clone. + /// The operation to perform on the clone. + /// The new . + public static Image Clone(this Image source, Action operation) + { + ProcessingVisitor visitor = new ProcessingVisitor(operation, false); + source.AcceptVisitor(visitor); + return visitor.ResultImage; + } + /// /// Creates a deep clone of the current image. The clone is then mutated by the given operation. /// @@ -149,5 +136,29 @@ namespace SixLabors.ImageSharp.Processing return source; } + + private class ProcessingVisitor : IImageVisitor + { + private readonly Action operation; + + private readonly bool mutate; + + public ProcessingVisitor(Action operation, bool mutate) + { + this.operation = operation; + this.mutate = mutate; + } + + public Image ResultImage { get; private set; } + + public void Visit(Image image) + where TPixel : struct, IPixel + { + IInternalImageProcessingContext operationsRunner = image.GetConfiguration() + .ImageOperationsProvider.CreateImageProcessingContext(image, this.mutate); + this.operation(operationsRunner); + this.ResultImage = operationsRunner.Apply(); + } + } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/BlackWhiteProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/BlackWhiteProcessor.cs index 8bf925cf0f..698f11cbf0 100644 --- a/src/ImageSharp/Processing/Processors/Filters/BlackWhiteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/BlackWhiteProcessor.cs @@ -8,11 +8,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies a black and white filter matrix to the image /// - /// The pixel format. internal class BlackWhiteProcessor : FilterProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public BlackWhiteProcessor() : base(KnownFilterMatrices.BlackWhiteFilter) diff --git a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs index 5637e97700..886b4e0962 100644 --- a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs @@ -6,6 +6,9 @@ using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Filters { + /// + /// Provides methods that accept a matrix to apply free-form filters to images. + /// public class FilterProcessor : IImageProcessor { /// @@ -19,6 +22,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// public ColorMatrix Matrix { get; } + /// public virtual IImageProcessor CreatePixelSpecificProcessor() where TPixel : struct, IPixel { diff --git a/src/ImageSharp/Processing/Processors/Filters/LomographProcessorImplementation.cs b/src/ImageSharp/Processing/Processors/Filters/LomographProcessorImplementation.cs index d851b98e06..a922e71256 100644 --- a/src/ImageSharp/Processing/Processors/Filters/LomographProcessorImplementation.cs +++ b/src/ImageSharp/Processing/Processors/Filters/LomographProcessorImplementation.cs @@ -7,20 +7,23 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Filters { + /// + /// Converts the colors of the image recreating an old Lomograph effect. + /// internal class LomographProcessorImplementation : FilterProcessorImplementation where TPixel : struct, IPixel { private static readonly TPixel VeryDarkGreen = ColorBuilder.FromRGBA(0, 10, 0, 255); - /// - protected override void AfterFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + public LomographProcessorImplementation(LomographProcessor definition) + : base(definition) { - new VignetteProcessor(VeryDarkGreen).Apply(source, sourceRectangle, configuration); } - public LomographProcessorImplementation(FilterProcessor definition) - : base(definition) + /// + protected override void AfterFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { + new VignetteProcessor(VeryDarkGreen).Apply(source, sourceRectangle, configuration); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs index 2d6bf6e833..e790753f3b 100644 --- a/src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs @@ -8,11 +8,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies an opacity filter matrix using the given amount. /// - /// The pixel format. internal class OpacityProcessor : FilterProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The proportion of the conversion. Must be between 0 and 1. public OpacityProcessor(float amount) diff --git a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs index af16d9eab2..0b272c3396 100644 --- a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs @@ -1,10 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing.Processors.Overlays; -using SixLabors.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// @@ -13,33 +9,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters public class PolaroidProcessor : FilterProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public PolaroidProcessor() : base(KnownFilterMatrices.PolaroidFilter) { } + /// public override IImageProcessor CreatePixelSpecificProcessor() => new PolaroidProcessorImplementation(this); } - - internal class PolaroidProcessorImplementation : FilterProcessorImplementation - where TPixel : struct, IPixel - { - private static readonly TPixel VeryDarkOrange = ColorBuilder.FromRGB(102, 34, 0); - private static readonly TPixel LightOrange = ColorBuilder.FromRGBA(255, 153, 102, 128); - - /// - protected override void AfterFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - { - new VignetteProcessor(VeryDarkOrange).Apply(source, sourceRectangle, configuration); - new GlowProcessor(LightOrange, source.Width / 4F).Apply(source, sourceRectangle, configuration); - } - - public PolaroidProcessorImplementation(FilterProcessor definition) - : base(definition) - { - } - } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessorImplementation.cs b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessorImplementation.cs new file mode 100644 index 0000000000..ec3c5ed26b --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessorImplementation.cs @@ -0,0 +1,31 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors.Overlays; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Filters +{ + /// + /// Converts the colors of the image recreating an old Polaroid effect. + /// + internal class PolaroidProcessorImplementation : FilterProcessorImplementation + where TPixel : struct, IPixel + { + private static readonly TPixel VeryDarkOrange = ColorBuilder.FromRGB(102, 34, 0); + private static readonly TPixel LightOrange = ColorBuilder.FromRGBA(255, 153, 102, 128); + + public PolaroidProcessorImplementation(FilterProcessor definition) + : base(definition) + { + } + + /// + protected override void AfterFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + new VignetteProcessor(VeryDarkOrange).Apply(source, sourceRectangle, configuration); + new GlowProcessor(LightOrange, source.Width / 4F).Apply(source, sourceRectangle, configuration); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/ProtanopiaProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/ProtanopiaProcessor.cs index 94eeaae475..bd92579130 100644 --- a/src/ImageSharp/Processing/Processors/Filters/ProtanopiaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/ProtanopiaProcessor.cs @@ -8,7 +8,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating Protanopia (Red-Blind) color blindness. /// - /// The pixel format. internal class ProtanopiaProcessor : FilterProcessor { /// diff --git a/src/ImageSharp/Processing/Processors/IImageProcessor.cs b/src/ImageSharp/Processing/Processors/IImageProcessor.cs index 68db453806..e9b2c9c0e3 100644 --- a/src/ImageSharp/Processing/Processors/IImageProcessor.cs +++ b/src/ImageSharp/Processing/Processors/IImageProcessor.cs @@ -6,14 +6,23 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors { + /// + /// Defines an algorithm to alter the pixels of an image. + /// public interface IImageProcessor { + /// + /// Creates a pixel specific that is capable for executing + /// the processing algorithm on an . + /// + /// The pixel type. + /// The IImageProcessor CreatePixelSpecificProcessor() where TPixel : struct, IPixel; } /// - /// Encapsulates methods to alter the pixels of an image. + /// Implements an algorithm to alter the pixels of an image. /// /// The pixel format. public interface IImageProcessor @@ -58,7 +67,7 @@ namespace SixLabors.ImageSharp.Processing.Processors public void Visit(Image image) where TPixel : struct, IPixel { - var processorImpl = processor.CreatePixelSpecificProcessor(); + var processorImpl = this.processor.CreatePixelSpecificProcessor(); processorImpl.Apply(image, this.sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs index d3077b7e65..021451b5c7 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs @@ -8,36 +8,20 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { - // The non-generic processor is responsible for: - // - Encapsulating the parameters of the processor - // - Implementing a factory method to create the pixel-specific processor that contains the implementation + /// + /// Implements resizing of images using various resamplers. + /// public class ResizeProcessor : IImageProcessor { /// - /// Gets the sampler to perform the resize operation. + /// Initializes a new instance of the class. /// - public IResampler Sampler { get; } - - /// - /// Gets the target width. - /// - public int Width { get; } - - /// - /// Gets the target height. - /// - public int Height { get; } - - /// - /// Gets the resize rectangle. - /// - public Rectangle TargetRectangle { get; } - - /// - /// Gets a value indicating whether to compress or expand individual pixel color values on processing. - /// - public bool Compand { get; } - + /// The . + /// The width. + /// The height. + /// The size of the source image. + /// The target rectangle to resize into. + /// A value indicating whether to apply RGBA companding. public ResizeProcessor(IResampler sampler, int width, int height, Size sourceSize, Rectangle targetRectangle, bool compand) { Guard.NotNull(sampler, nameof(sampler)); @@ -67,12 +51,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms this.TargetRectangle = targetRectangle; this.Compand = compand; } - + /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// The resize options - /// The source image size + /// The resize options. + /// The source image size. public ResizeProcessor(ResizeOptions options, Size sourceSize) { Guard.NotNull(options, nameof(options)); @@ -109,7 +93,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The sampler to perform the resize operation. /// The target width. @@ -120,6 +104,32 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms { } + /// + /// Gets the sampler to perform the resize operation. + /// + public IResampler Sampler { get; } + + /// + /// Gets the target width. + /// + public int Width { get; } + + /// + /// Gets the target height. + /// + public int Height { get; } + + /// + /// Gets the resize rectangle. + /// + public Rectangle TargetRectangle { get; } + + /// + /// Gets a value indicating whether to compress or expand individual pixel color values on processing. + /// + public bool Compand { get; } + + /// public IImageProcessor CreatePixelSpecificProcessor() where TPixel : struct, IPixel { diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessorImplementation.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessorImplementation.cs index 1a8bb931e0..36a054c54e 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessorImplementation.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessorImplementation.cs @@ -19,9 +19,11 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { /// - /// Provides methods that allow the resizing of images using various algorithms. - /// Adapted from + /// Implements resizing of images using various resamplers. /// + /// + /// The original code has been adapted from . + /// /// The pixel format. internal class ResizeProcessorImplementation : TransformProcessorBase where TPixel : struct, IPixel diff --git a/src/ImageSharp/Processing/ResizeExtensions.cs b/src/ImageSharp/Processing/ResizeExtensions.cs index cf029eb152..57dbcb1d16 100644 --- a/src/ImageSharp/Processing/ResizeExtensions.cs +++ b/src/ImageSharp/Processing/ResizeExtensions.cs @@ -15,7 +15,6 @@ namespace SixLabors.ImageSharp.Processing /// /// Resizes an image in accordance with the given . /// - /// The pixel format. /// The image to resize. /// The resize options. /// The @@ -26,7 +25,6 @@ namespace SixLabors.ImageSharp.Processing /// /// Resizes an image to the given . /// - /// The pixel format. /// The image to resize. /// The target image size. /// The @@ -37,7 +35,6 @@ namespace SixLabors.ImageSharp.Processing /// /// Resizes an image to the given . /// - /// The pixel format. /// The image to resize. /// The target image size. /// Whether to compress and expand the image color-space to gamma correct the image during processing. @@ -49,7 +46,6 @@ namespace SixLabors.ImageSharp.Processing /// /// Resizes an image to the given width and height. /// - /// The pixel format. /// The image to resize. /// The target image width. /// The target image height. @@ -61,7 +57,6 @@ namespace SixLabors.ImageSharp.Processing /// /// Resizes an image to the given width and height. /// - /// The pixel format. /// The image to resize. /// The target image width. /// The target image height. @@ -74,7 +69,6 @@ namespace SixLabors.ImageSharp.Processing /// /// Resizes an image to the given width and height with the given sampler. /// - /// The pixel format. /// The image to resize. /// The target image width. /// The target image height. @@ -87,7 +81,6 @@ namespace SixLabors.ImageSharp.Processing /// /// Resizes an image to the given width and height with the given sampler. /// - /// The pixel format. /// The image to resize. /// The target image size. /// The to perform the resampling. @@ -100,7 +93,6 @@ namespace SixLabors.ImageSharp.Processing /// /// Resizes an image to the given width and height with the given sampler. /// - /// The pixel format. /// The image to resize. /// The target image width. /// The target image height. @@ -115,7 +107,6 @@ namespace SixLabors.ImageSharp.Processing /// Resizes an image to the given width and height with the given sampler and /// source rectangle. /// - /// The pixel format. /// The image to resize. /// The target image width. /// The target image height. @@ -142,7 +133,6 @@ namespace SixLabors.ImageSharp.Processing /// /// Resizes an image to the given width and height with the given sampler and source rectangle. /// - /// The pixel format. /// The image to resize. /// The target image width. /// The target image height. diff --git a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj index 9af4d57cff..2a5408da17 100644 --- a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj +++ b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj @@ -1,6 +1,7 @@  - netcoreapp2.1;net472 + + netcoreapp2.1 Exe True SixLabors.ImageSharp.Benchmarks From 2995f596ef77a260ead68c506f91126f6ee8f733 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 4 May 2019 14:34:11 +0200 Subject: [PATCH 111/223] *ProcessorImplementation ===> *Processor, add suppression of SA1413 to AssemblyInfo.cs --- .../Processing/Processors/Filters/FilterProcessor.cs | 2 +- ...sorImplementation.cs => FilterProcessor{TPixel}.cs} | 4 ++-- .../Processors/Filters/GrayscaleBt709Processor.cs | 2 +- .../Processors/Filters/LomographProcessor.cs | 2 +- ...Implementation.cs => LomographProcessor{TPixel}.cs} | 4 ++-- .../Processing/Processors/Filters/PolaroidProcessor.cs | 2 +- ...rImplementation.cs => PolaroidProcessor{TPixel}.cs} | 4 ++-- .../Processors/Transforms/Resize/ResizeProcessor.cs | 2 +- ...sorImplementation.cs => ResizeProcessor{TPixel}.cs} | 4 ++-- src/ImageSharp/Properties/AssemblyInfo.cs | 10 +++++++++- .../ImageSharp.Tests/Processing/Transforms/PadTest.cs | 2 +- 11 files changed, 23 insertions(+), 15 deletions(-) rename src/ImageSharp/Processing/Processors/Filters/{FilterProcessorImplementation.cs => FilterProcessor{TPixel}.cs} (92%) rename src/ImageSharp/Processing/Processors/Filters/{LomographProcessorImplementation.cs => LomographProcessor{TPixel}.cs} (83%) rename src/ImageSharp/Processing/Processors/Filters/{PolaroidProcessorImplementation.cs => PolaroidProcessor{TPixel}.cs} (86%) rename src/ImageSharp/Processing/Processors/Transforms/Resize/{ResizeProcessorImplementation.cs => ResizeProcessor{TPixel}.cs} (97%) diff --git a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs index 886b4e0962..b615b7bc60 100644 --- a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs @@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters public virtual IImageProcessor CreatePixelSpecificProcessor() where TPixel : struct, IPixel { - return new FilterProcessorImplementation(this); + return new FilterProcessor(this); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/FilterProcessorImplementation.cs b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs similarity index 92% rename from src/ImageSharp/Processing/Processors/Filters/FilterProcessorImplementation.cs rename to src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs index 8fea81564c..ab60a42793 100644 --- a/src/ImageSharp/Processing/Processors/Filters/FilterProcessorImplementation.cs +++ b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs @@ -15,12 +15,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// Provides methods that accept a matrix to apply free-form filters to images. /// /// The pixel format. - internal class FilterProcessorImplementation : ImageProcessor + internal class FilterProcessor : ImageProcessor where TPixel : struct, IPixel { private readonly FilterProcessor definition; - public FilterProcessorImplementation(FilterProcessor definition) + public FilterProcessor(FilterProcessor definition) { this.definition = definition; } diff --git a/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs b/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs index 038a2885bc..7bbd915564 100644 --- a/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs @@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters internal void ApplyToFrame(ImageFrame frame, Rectangle sourceRectangle, Configuration configuration) where TPixel : struct, IPixel { - var processorImpl = new FilterProcessorImplementation(new GrayscaleBt709Processor(1F)); + var processorImpl = new FilterProcessor(new GrayscaleBt709Processor(1F)); processorImpl.Apply(frame, sourceRectangle, configuration); } } diff --git a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs index 345d829ac6..b102f4daf7 100644 --- a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs @@ -17,6 +17,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters } public override IImageProcessor CreatePixelSpecificProcessor() => - new LomographProcessorImplementation(this); + new LomographProcessor(this); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/LomographProcessorImplementation.cs b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs similarity index 83% rename from src/ImageSharp/Processing/Processors/Filters/LomographProcessorImplementation.cs rename to src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs index a922e71256..8ec4ec258a 100644 --- a/src/ImageSharp/Processing/Processors/Filters/LomographProcessorImplementation.cs +++ b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs @@ -10,12 +10,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating an old Lomograph effect. /// - internal class LomographProcessorImplementation : FilterProcessorImplementation + internal class LomographProcessor : FilterProcessor where TPixel : struct, IPixel { private static readonly TPixel VeryDarkGreen = ColorBuilder.FromRGBA(0, 10, 0, 255); - public LomographProcessorImplementation(LomographProcessor definition) + public LomographProcessor(LomographProcessor definition) : base(definition) { } diff --git a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs index 0b272c3396..341c02acfc 100644 --- a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs @@ -18,6 +18,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// public override IImageProcessor CreatePixelSpecificProcessor() => - new PolaroidProcessorImplementation(this); + new PolaroidProcessor(this); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessorImplementation.cs b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs similarity index 86% rename from src/ImageSharp/Processing/Processors/Filters/PolaroidProcessorImplementation.cs rename to src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs index ec3c5ed26b..a73aa28de3 100644 --- a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessorImplementation.cs +++ b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs @@ -10,13 +10,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating an old Polaroid effect. /// - internal class PolaroidProcessorImplementation : FilterProcessorImplementation + internal class PolaroidProcessor : FilterProcessor where TPixel : struct, IPixel { private static readonly TPixel VeryDarkOrange = ColorBuilder.FromRGB(102, 34, 0); private static readonly TPixel LightOrange = ColorBuilder.FromRGBA(255, 153, 102, 128); - public PolaroidProcessorImplementation(FilterProcessor definition) + public PolaroidProcessor(FilterProcessor definition) : base(definition) { } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs index 021451b5c7..5710c4bd8c 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs @@ -133,7 +133,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public IImageProcessor CreatePixelSpecificProcessor() where TPixel : struct, IPixel { - return new ResizeProcessorImplementation(this); + return new ResizeProcessor(this); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessorImplementation.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs similarity index 97% rename from src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessorImplementation.cs rename to src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs index 36a054c54e..cf99f28dd7 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessorImplementation.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// The original code has been adapted from . /// /// The pixel format. - internal class ResizeProcessorImplementation : TransformProcessorBase + internal class ResizeProcessor : TransformProcessorBase where TPixel : struct, IPixel { // The following fields are not immutable but are optionally created on demand. @@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms private readonly ResizeProcessor parameterSource; - public ResizeProcessorImplementation(ResizeProcessor parameterSource) + public ResizeProcessor(ResizeProcessor parameterSource) { this.parameterSource = parameterSource; } diff --git a/src/ImageSharp/Properties/AssemblyInfo.cs b/src/ImageSharp/Properties/AssemblyInfo.cs index 7b8f933b0c..f4459887f0 100644 --- a/src/ImageSharp/Properties/AssemblyInfo.cs +++ b/src/ImageSharp/Properties/AssemblyInfo.cs @@ -1,6 +1,14 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. + using System.Runtime.CompilerServices; // Ensure the other projects can see the internal helpers -[assembly: InternalsVisibleTo("SixLabors.ImageSharp.Drawing")] \ No newline at end of file +[assembly: InternalsVisibleTo("SixLabors.ImageSharp.Drawing")] + +// Redundant suppressing of SA1413 for Rider. +[assembly: + System.Diagnostics.CodeAnalysis.SuppressMessage( + "StyleCop.CSharp.MaintainabilityRules", + "SA1413:UseTrailingCommasInMultiLineInitializers", + Justification = "Follows SixLabors.ruleset")] \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs index eca1a458b1..82d7682558 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs @@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms IResampler sampler = KnownResamplers.NearestNeighbor; this.operations.Pad(width, height); - ResizeProcessorImplementation resizeProcessor = this.Verify>(); + ResizeProcessor resizeProcessor = this.Verify>(); Assert.Equal(width, resizeProcessor.Width); Assert.Equal(height, resizeProcessor.Height); From 8b0416f13817012696211bf309b8c778e85d7867 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 4 May 2019 17:02:46 +0200 Subject: [PATCH 112/223] validating tests for Convolution processors --- src/ImageSharp/Common/Helpers/Vector4Utils.cs | 2 +- .../Convolution/EdgeDetector2DProcessor.cs | 2 +- .../Basic1ParameterConvolutionTests.cs | 56 +++++++++++++++++++ .../Processors/Convolution/BoxBlurTest.cs | 44 ++------------- .../Processors/Convolution/DetectEdgesTest.cs | 19 ++++--- .../Convolution/GaussianBlurTest.cs | 35 ++---------- .../Convolution/GaussianSharpenTest.cs | 40 ++----------- tests/Images/External | 2 +- 8 files changed, 85 insertions(+), 115 deletions(-) create mode 100644 tests/ImageSharp.Tests/Processing/Processors/Convolution/Basic1ParameterConvolutionTests.cs diff --git a/src/ImageSharp/Common/Helpers/Vector4Utils.cs b/src/ImageSharp/Common/Helpers/Vector4Utils.cs index 5c122217d1..a4e0921d0a 100644 --- a/src/ImageSharp/Common/Helpers/Vector4Utils.cs +++ b/src/ImageSharp/Common/Helpers/Vector4Utils.cs @@ -92,7 +92,7 @@ namespace SixLabors.ImageSharp } /// - /// Bulk variant of + /// Bulk variant of . /// /// The span of vectors /// The transformation matrix. diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs index c08a176618..b4ac89139a 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs @@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution public DenseMatrix KernelY { get; } /// - public bool Grayscale { get; set; } + public bool Grayscale { get; } /// protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/Basic1ParameterConvolutionTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/Basic1ParameterConvolutionTests.cs new file mode 100644 index 0000000000..1f939a281b --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/Basic1ParameterConvolutionTests.cs @@ -0,0 +1,56 @@ +// // Copyright (c) Six Labors and contributors. +// // Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; +using SixLabors.Primitives; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution +{ + [GroupOutput("Convolution")] + public abstract class Basic1ParameterConvolutionTests + { + private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.05F); + + public static readonly TheoryData Values = new TheoryData { 3, 5 }; + + public static readonly string[] InputImages = + { + TestImages.Bmp.Car, + TestImages.Png.CalliphoraPartial + }; + + [Theory] + [WithFileCollection(nameof(InputImages), nameof(Values), PixelTypes.Rgba32)] + public void OnFullImage(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + provider.Utility.TestGroupName = this.GetType().Name; + provider.RunValidatingProcessorTest( + x => this.Apply(x, value), + value, + ValidatorComparer); + } + + [Theory] + [WithFileCollection(nameof(InputImages), nameof(Values), PixelTypes.Rgba32)] + public void InBox(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + provider.Utility.TestGroupName = this.GetType().Name; + provider.RunRectangleConstrainedValidatingProcessorTest( + (x, rect) => this.Apply(x, value, rect), + value, + ValidatorComparer); + } + + protected abstract void Apply(IImageProcessingContext ctx, int value) + where TPixel : struct, IPixel; + + protected abstract void Apply(IImageProcessingContext ctx, int value, Rectangle bounds) + where TPixel : struct, IPixel; + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BoxBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BoxBlurTest.cs index 1f0d12cf7f..923f9d6161 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BoxBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BoxBlurTest.cs @@ -1,51 +1,17 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; - using SixLabors.Primitives; -using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution { - public class BoxBlurTest : FileTestBase + [GroupOutput("Convolution")] + public class BoxBlurTest : Basic1ParameterConvolutionTests { - public static readonly TheoryData BoxBlurValues - = new TheoryData - { - 3, - 5 - }; - - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(BoxBlurValues), DefaultPixelType)] - public void ImageShouldApplyBoxBlurFilter(TestImageProvider provider, int value) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Mutate(x => x.BoxBlur(value)); - image.DebugSave(provider, value); - } - } - - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(BoxBlurValues), DefaultPixelType)] - public void ImageShouldApplyBoxBlurFilterInBox(TestImageProvider provider, int value) - where TPixel : struct, IPixel - { - using (Image source = provider.GetImage()) - using (Image image = source.Clone()) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Mutate(x => x.BoxBlur(value, bounds)); - image.DebugSave(provider, value); + protected override void Apply(IImageProcessingContext ctx, int value) => ctx.BoxBlur(value); - ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); - } - } + protected override void Apply(IImageProcessingContext ctx, int value, Rectangle bounds) => + ctx.BoxBlur(value, bounds); } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs index b6a7741b3c..05524b20b0 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs @@ -10,13 +10,16 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution { - public class DetectEdgesTest : FileTestBase + [GroupOutput("Convolution")] + public class DetectEdgesTest { // I think our comparison is not accurate enough (nor can be) for RgbaVector. // The image pixels are identical according to BeyondCompare. private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.0456F); - public static readonly string[] CommonTestImages = { TestImages.Png.Bike }; + public static readonly string[] TestImages = { Tests.TestImages.Png.Bike }; + + public const PixelTypes CommonNonDefaultPixelTypes = PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.RgbaVector; public static readonly TheoryData DetectEdgesFilters = new TheoryData { @@ -33,7 +36,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution }; [Theory] - [WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] + [WithFileCollection(nameof(TestImages), PixelTypes.Rgba32)] public void DetectEdges_WorksOnWrappedMemoryImage(TestImageProvider provider) where TPixel : struct, IPixel { @@ -49,8 +52,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution } [Theory] - [WithTestPatternImages(nameof(DetectEdgesFilters), 100, 100, DefaultPixelType)] - [WithFileCollection(nameof(CommonTestImages), nameof(DetectEdgesFilters), DefaultPixelType)] + [WithTestPatternImages(nameof(DetectEdgesFilters), 100, 100, PixelTypes.Rgba32)] + [WithFileCollection(nameof(TestImages), nameof(DetectEdgesFilters), PixelTypes.Rgba32)] public void DetectEdges_WorksWithAllFilters(TestImageProvider provider, EdgeDetectionOperators detector) where TPixel : struct, IPixel { @@ -63,7 +66,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution } [Theory] - [WithFileCollection(nameof(CommonTestImages), CommonNonDefaultPixelTypes)] + [WithFileCollection(nameof(TestImages), CommonNonDefaultPixelTypes)] public void DetectEdges_IsNotBoundToSinglePixelType(TestImageProvider provider) where TPixel : struct, IPixel { @@ -76,7 +79,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution } [Theory] - [WithFile(TestImages.Gif.Giphy, DefaultPixelType)] + [WithFile(Tests.TestImages.Gif.Giphy, PixelTypes.Rgba32)] public void DetectEdges_IsAppliedToAllFrames(TestImageProvider provider) where TPixel : struct, IPixel { @@ -88,7 +91,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution } [Theory] - [WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] + [WithFileCollection(nameof(TestImages), PixelTypes.Rgba32)] public void DetectEdges_InBox(TestImageProvider provider) where TPixel : struct, IPixel { diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs index 6bd3b34bb1..6307a1c51d 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs @@ -10,37 +10,12 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution { - public class GaussianBlurTest : FileTestBase + [GroupOutput("Convolution")] + public class GaussianBlurTest : Basic1ParameterConvolutionTests { - public static readonly TheoryData GaussianBlurValues = new TheoryData { 3, 5 }; + protected override void Apply(IImageProcessingContext ctx, int value) => ctx.GaussianBlur(value); - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(GaussianBlurValues), DefaultPixelType)] - public void ImageShouldApplyGaussianBlurFilter(TestImageProvider provider, int value) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Mutate(x => x.GaussianBlur(value)); - image.DebugSave(provider, value); - } - } - - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(GaussianBlurValues), DefaultPixelType)] - public void ImageShouldApplyGaussianBlurFilterInBox(TestImageProvider provider, int value) - where TPixel : struct, IPixel - { - using (Image source = provider.GetImage()) - using (Image image = source.Clone()) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Mutate(x => x.GaussianBlur(value, bounds)); - image.DebugSave(provider, value); - - ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); - } - } + protected override void Apply(IImageProcessingContext ctx, int value, Rectangle bounds) => + ctx.GaussianBlur(value, bounds); } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianSharpenTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianSharpenTest.cs index 8eb1f85eb2..29a1643b0c 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianSharpenTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianSharpenTest.cs @@ -9,42 +9,12 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution { - public class GaussianSharpenTest : FileTestBase + [GroupOutput("Convolution")] + public class GaussianSharpenTest : Basic1ParameterConvolutionTests { - public static readonly TheoryData GaussianSharpenValues - = new TheoryData - { - 3, - 5 - }; + protected override void Apply(IImageProcessingContext ctx, int value) => ctx.GaussianSharpen(value); - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(GaussianSharpenValues), DefaultPixelType)] - public void ImageShouldApplyGaussianSharpenFilter(TestImageProvider provider, int value) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Mutate(x => x.GaussianSharpen(value)); - image.DebugSave(provider, value); - } - } - - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(GaussianSharpenValues), DefaultPixelType)] - public void ImageShouldApplyGaussianSharpenFilterInBox(TestImageProvider provider, int value) - where TPixel : struct, IPixel - { - using (Image source = provider.GetImage()) - using (var image = source.Clone()) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Mutate(x => x.GaussianSharpen(value, bounds)); - image.DebugSave(provider, value); - - ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); - } - } + protected override void Apply(IImageProcessingContext ctx, int value, Rectangle bounds) => + ctx.GaussianSharpen(value, bounds); } } \ No newline at end of file diff --git a/tests/Images/External b/tests/Images/External index 1ca5154996..2a3247c6da 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 1ca515499663e8b0b7c924a49b8d212f7447bdb0 +Subproject commit 2a3247c6da18b8a96cc71e1f2862ac03f2a42315 From 01ae31cd146a89c5f93fce35a31041cdcf4d132b Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 4 May 2019 17:05:48 +0200 Subject: [PATCH 113/223] validating tests for Convolution processors --- src/ImageSharp/Common/Helpers/Vector4Utils.cs | 2 +- .../Convolution/EdgeDetector2DProcessor.cs | 2 +- .../Basic1ParameterConvolutionTests.cs | 56 +++++++++++++++++++ .../Processors/Convolution/BoxBlurTest.cs | 44 ++------------- .../Processors/Convolution/DetectEdgesTest.cs | 19 ++++--- .../Convolution/GaussianBlurTest.cs | 35 ++---------- .../Convolution/GaussianSharpenTest.cs | 40 ++----------- tests/Images/External | 2 +- 8 files changed, 85 insertions(+), 115 deletions(-) create mode 100644 tests/ImageSharp.Tests/Processing/Processors/Convolution/Basic1ParameterConvolutionTests.cs diff --git a/src/ImageSharp/Common/Helpers/Vector4Utils.cs b/src/ImageSharp/Common/Helpers/Vector4Utils.cs index 5c122217d1..a4e0921d0a 100644 --- a/src/ImageSharp/Common/Helpers/Vector4Utils.cs +++ b/src/ImageSharp/Common/Helpers/Vector4Utils.cs @@ -92,7 +92,7 @@ namespace SixLabors.ImageSharp } /// - /// Bulk variant of + /// Bulk variant of . /// /// The span of vectors /// The transformation matrix. diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs index d2e9630dc0..83746952cb 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs @@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution public DenseMatrix KernelY { get; } /// - public bool Grayscale { get; set; } + public bool Grayscale { get; } /// protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/Basic1ParameterConvolutionTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/Basic1ParameterConvolutionTests.cs new file mode 100644 index 0000000000..1f939a281b --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/Basic1ParameterConvolutionTests.cs @@ -0,0 +1,56 @@ +// // Copyright (c) Six Labors and contributors. +// // Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; +using SixLabors.Primitives; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution +{ + [GroupOutput("Convolution")] + public abstract class Basic1ParameterConvolutionTests + { + private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.05F); + + public static readonly TheoryData Values = new TheoryData { 3, 5 }; + + public static readonly string[] InputImages = + { + TestImages.Bmp.Car, + TestImages.Png.CalliphoraPartial + }; + + [Theory] + [WithFileCollection(nameof(InputImages), nameof(Values), PixelTypes.Rgba32)] + public void OnFullImage(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + provider.Utility.TestGroupName = this.GetType().Name; + provider.RunValidatingProcessorTest( + x => this.Apply(x, value), + value, + ValidatorComparer); + } + + [Theory] + [WithFileCollection(nameof(InputImages), nameof(Values), PixelTypes.Rgba32)] + public void InBox(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + provider.Utility.TestGroupName = this.GetType().Name; + provider.RunRectangleConstrainedValidatingProcessorTest( + (x, rect) => this.Apply(x, value, rect), + value, + ValidatorComparer); + } + + protected abstract void Apply(IImageProcessingContext ctx, int value) + where TPixel : struct, IPixel; + + protected abstract void Apply(IImageProcessingContext ctx, int value, Rectangle bounds) + where TPixel : struct, IPixel; + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BoxBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BoxBlurTest.cs index 1f0d12cf7f..923f9d6161 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BoxBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BoxBlurTest.cs @@ -1,51 +1,17 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; - using SixLabors.Primitives; -using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution { - public class BoxBlurTest : FileTestBase + [GroupOutput("Convolution")] + public class BoxBlurTest : Basic1ParameterConvolutionTests { - public static readonly TheoryData BoxBlurValues - = new TheoryData - { - 3, - 5 - }; - - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(BoxBlurValues), DefaultPixelType)] - public void ImageShouldApplyBoxBlurFilter(TestImageProvider provider, int value) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Mutate(x => x.BoxBlur(value)); - image.DebugSave(provider, value); - } - } - - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(BoxBlurValues), DefaultPixelType)] - public void ImageShouldApplyBoxBlurFilterInBox(TestImageProvider provider, int value) - where TPixel : struct, IPixel - { - using (Image source = provider.GetImage()) - using (Image image = source.Clone()) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Mutate(x => x.BoxBlur(value, bounds)); - image.DebugSave(provider, value); + protected override void Apply(IImageProcessingContext ctx, int value) => ctx.BoxBlur(value); - ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); - } - } + protected override void Apply(IImageProcessingContext ctx, int value, Rectangle bounds) => + ctx.BoxBlur(value, bounds); } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs index b6a7741b3c..05524b20b0 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs @@ -10,13 +10,16 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution { - public class DetectEdgesTest : FileTestBase + [GroupOutput("Convolution")] + public class DetectEdgesTest { // I think our comparison is not accurate enough (nor can be) for RgbaVector. // The image pixels are identical according to BeyondCompare. private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.0456F); - public static readonly string[] CommonTestImages = { TestImages.Png.Bike }; + public static readonly string[] TestImages = { Tests.TestImages.Png.Bike }; + + public const PixelTypes CommonNonDefaultPixelTypes = PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.RgbaVector; public static readonly TheoryData DetectEdgesFilters = new TheoryData { @@ -33,7 +36,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution }; [Theory] - [WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] + [WithFileCollection(nameof(TestImages), PixelTypes.Rgba32)] public void DetectEdges_WorksOnWrappedMemoryImage(TestImageProvider provider) where TPixel : struct, IPixel { @@ -49,8 +52,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution } [Theory] - [WithTestPatternImages(nameof(DetectEdgesFilters), 100, 100, DefaultPixelType)] - [WithFileCollection(nameof(CommonTestImages), nameof(DetectEdgesFilters), DefaultPixelType)] + [WithTestPatternImages(nameof(DetectEdgesFilters), 100, 100, PixelTypes.Rgba32)] + [WithFileCollection(nameof(TestImages), nameof(DetectEdgesFilters), PixelTypes.Rgba32)] public void DetectEdges_WorksWithAllFilters(TestImageProvider provider, EdgeDetectionOperators detector) where TPixel : struct, IPixel { @@ -63,7 +66,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution } [Theory] - [WithFileCollection(nameof(CommonTestImages), CommonNonDefaultPixelTypes)] + [WithFileCollection(nameof(TestImages), CommonNonDefaultPixelTypes)] public void DetectEdges_IsNotBoundToSinglePixelType(TestImageProvider provider) where TPixel : struct, IPixel { @@ -76,7 +79,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution } [Theory] - [WithFile(TestImages.Gif.Giphy, DefaultPixelType)] + [WithFile(Tests.TestImages.Gif.Giphy, PixelTypes.Rgba32)] public void DetectEdges_IsAppliedToAllFrames(TestImageProvider provider) where TPixel : struct, IPixel { @@ -88,7 +91,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution } [Theory] - [WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] + [WithFileCollection(nameof(TestImages), PixelTypes.Rgba32)] public void DetectEdges_InBox(TestImageProvider provider) where TPixel : struct, IPixel { diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs index 6bd3b34bb1..6307a1c51d 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs @@ -10,37 +10,12 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution { - public class GaussianBlurTest : FileTestBase + [GroupOutput("Convolution")] + public class GaussianBlurTest : Basic1ParameterConvolutionTests { - public static readonly TheoryData GaussianBlurValues = new TheoryData { 3, 5 }; + protected override void Apply(IImageProcessingContext ctx, int value) => ctx.GaussianBlur(value); - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(GaussianBlurValues), DefaultPixelType)] - public void ImageShouldApplyGaussianBlurFilter(TestImageProvider provider, int value) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Mutate(x => x.GaussianBlur(value)); - image.DebugSave(provider, value); - } - } - - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(GaussianBlurValues), DefaultPixelType)] - public void ImageShouldApplyGaussianBlurFilterInBox(TestImageProvider provider, int value) - where TPixel : struct, IPixel - { - using (Image source = provider.GetImage()) - using (Image image = source.Clone()) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Mutate(x => x.GaussianBlur(value, bounds)); - image.DebugSave(provider, value); - - ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); - } - } + protected override void Apply(IImageProcessingContext ctx, int value, Rectangle bounds) => + ctx.GaussianBlur(value, bounds); } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianSharpenTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianSharpenTest.cs index 8eb1f85eb2..29a1643b0c 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianSharpenTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianSharpenTest.cs @@ -9,42 +9,12 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution { - public class GaussianSharpenTest : FileTestBase + [GroupOutput("Convolution")] + public class GaussianSharpenTest : Basic1ParameterConvolutionTests { - public static readonly TheoryData GaussianSharpenValues - = new TheoryData - { - 3, - 5 - }; + protected override void Apply(IImageProcessingContext ctx, int value) => ctx.GaussianSharpen(value); - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(GaussianSharpenValues), DefaultPixelType)] - public void ImageShouldApplyGaussianSharpenFilter(TestImageProvider provider, int value) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Mutate(x => x.GaussianSharpen(value)); - image.DebugSave(provider, value); - } - } - - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(GaussianSharpenValues), DefaultPixelType)] - public void ImageShouldApplyGaussianSharpenFilterInBox(TestImageProvider provider, int value) - where TPixel : struct, IPixel - { - using (Image source = provider.GetImage()) - using (var image = source.Clone()) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Mutate(x => x.GaussianSharpen(value, bounds)); - image.DebugSave(provider, value); - - ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); - } - } + protected override void Apply(IImageProcessingContext ctx, int value, Rectangle bounds) => + ctx.GaussianSharpen(value, bounds); } } \ No newline at end of file diff --git a/tests/Images/External b/tests/Images/External index 1ca5154996..2a3247c6da 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 1ca515499663e8b0b7c924a49b8d212f7447bdb0 +Subproject commit 2a3247c6da18b8a96cc71e1f2862ac03f2a42315 From 655ef9a3243d0a8c60b1c2dc98d14f1b4628eed8 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 4 May 2019 17:09:25 +0200 Subject: [PATCH 114/223] mark FileTestBase obsolete --- tests/ImageSharp.Tests/FileTestBase.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/ImageSharp.Tests/FileTestBase.cs b/tests/ImageSharp.Tests/FileTestBase.cs index efd6c3b40e..a056bc474e 100644 --- a/tests/ImageSharp.Tests/FileTestBase.cs +++ b/tests/ImageSharp.Tests/FileTestBase.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; using System.Collections.Generic; namespace SixLabors.ImageSharp.Tests @@ -8,6 +9,7 @@ namespace SixLabors.ImageSharp.Tests /// /// The test base class for reading and writing to files. /// + [Obsolete("See: https://github.com/SixLabors/ImageSharp/issues/868")] public abstract class FileTestBase { /// From 21346b57e70a4296055799db5386ae55c15fb16a Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 4 May 2019 17:30:35 +0200 Subject: [PATCH 115/223] improve DitherTests CQ --- .../Processors/Dithering/DitherTests.cs | 133 +++++++++--------- 1 file changed, 68 insertions(+), 65 deletions(-) diff --git a/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs index 9774cb50cf..fbe7bb1822 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs @@ -2,128 +2,131 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Dithering; using SixLabors.ImageSharp.Processing.Processors.Dithering; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using SixLabors.Primitives; + using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization { - public class DitherTests : FileTestBase + public class DitherTests { - public static readonly string[] CommonTestImages = - { - TestImages.Png.CalliphoraPartial, TestImages.Png.Bike - }; - - public static readonly TheoryData OrderedDitherers = new TheoryData - { - { "Bayer8x8", KnownDitherers.BayerDither8x8 }, - { "Bayer4x4", KnownDitherers.BayerDither4x4 }, - { "Ordered3x3", KnownDitherers.OrderedDither3x3 }, - { "Bayer2x2", KnownDitherers.BayerDither2x2 } - }; - - public static readonly TheoryData ErrorDiffusers = new TheoryData - { - { "Atkinson", KnownDiffusers.Atkinson }, - { "Burks", KnownDiffusers.Burks }, - { "FloydSteinberg", KnownDiffusers.FloydSteinberg }, - { "JarvisJudiceNinke", KnownDiffusers.JarvisJudiceNinke }, - { "Sierra2", KnownDiffusers.Sierra2 }, - { "Sierra3", KnownDiffusers.Sierra3 }, - { "SierraLite", KnownDiffusers.SierraLite }, - { "StevensonArce", KnownDiffusers.StevensonArce }, - { "Stucki", KnownDiffusers.Stucki }, - }; - + public const PixelTypes CommonNonDefaultPixelTypes = + PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.Rgb24 | PixelTypes.RgbaVector; + + public static readonly string[] CommonTestImages = { TestImages.Png.CalliphoraPartial, TestImages.Png.Bike }; + + public static readonly TheoryData ErrorDiffusers = new TheoryData + { + KnownDiffusers.Atkinson, + KnownDiffusers.Burks, + KnownDiffusers.FloydSteinberg, + KnownDiffusers.JarvisJudiceNinke, + KnownDiffusers.Sierra2, + KnownDiffusers.Sierra3, + KnownDiffusers.SierraLite, + KnownDiffusers.StevensonArce, + KnownDiffusers.Stucki, + }; + + public static readonly TheoryData OrderedDitherers = new TheoryData + { + KnownDitherers.BayerDither8x8, + KnownDitherers.BayerDither4x4, + KnownDitherers.OrderedDither3x3, + KnownDitherers.BayerDither2x2 + }; + private static IOrderedDither DefaultDitherer => KnownDitherers.BayerDither4x4; private static IErrorDiffuser DefaultErrorDiffuser => KnownDiffusers.Atkinson; [Theory] - [WithFileCollection(nameof(CommonTestImages), nameof(OrderedDitherers), DefaultPixelType)] - [WithTestPatternImages(nameof(OrderedDitherers), 100, 100, DefaultPixelType)] - public void DitherFilter_WorksWithAllDitherers(TestImageProvider provider, string name, IOrderedDither ditherer) + [WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Rgba32)] + public void ApplyDiffusionFilterInBox(TestImageProvider provider) where TPixel : struct, IPixel { - using (Image image = provider.GetImage()) + using (Image source = provider.GetImage()) + using (Image image = source.Clone()) { - image.Mutate(x => x.Dither(ditherer)); - image.DebugSave(provider, name); + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Mutate(x => x.Diffuse(DefaultErrorDiffuser, .5F, bounds)); + image.DebugSave(provider); + + ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); } } [Theory] - [WithFileCollection(nameof(CommonTestImages), nameof(ErrorDiffusers), DefaultPixelType)] - [WithTestPatternImages(nameof(ErrorDiffusers), 100, 100, DefaultPixelType)] - public void DiffusionFilter_WorksWithAllErrorDiffusers(TestImageProvider provider, string name, IErrorDiffuser diffuser) + [WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Rgba32)] + public void ApplyDitherFilterInBox(TestImageProvider provider) where TPixel : struct, IPixel { - using (Image image = provider.GetImage()) + using (Image source = provider.GetImage()) + using (Image image = source.Clone()) { - image.Mutate(x => x.Diffuse(diffuser, .5F)); - image.DebugSave(provider, name); + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Mutate(x => x.Dither(DefaultDitherer, bounds)); + image.DebugSave(provider); + + ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); } } [Theory] [WithFile(TestImages.Png.Filter0, CommonNonDefaultPixelTypes)] - public void DitherFilter_ShouldNotDependOnSinglePixelType(TestImageProvider provider) + public void DiffusionFilter_ShouldNotDependOnSinglePixelType(TestImageProvider provider) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) { - image.Mutate(x => x.Dither(DefaultDitherer)); + image.Mutate(x => x.Diffuse(DefaultErrorDiffuser, 0.5f)); image.DebugSave(provider); } } [Theory] - [WithFile(TestImages.Png.Filter0, CommonNonDefaultPixelTypes)] - public void DiffusionFilter_ShouldNotDependOnSinglePixelType(TestImageProvider provider) + [WithFileCollection(nameof(CommonTestImages), nameof(ErrorDiffusers), PixelTypes.Rgba32)] + public void DiffusionFilter_WorksWithAllErrorDiffusers( + TestImageProvider provider, + IErrorDiffuser diffuser) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) { - image.Mutate(x => x.Diffuse(DefaultErrorDiffuser, 0.5f)); - image.DebugSave(provider); + image.Mutate(x => x.Diffuse(diffuser, .5F)); + image.DebugSave(provider, diffuser.GetType().Name); } } [Theory] - [WithFile(TestImages.Png.CalliphoraPartial, DefaultPixelType)] - public void ApplyDitherFilterInBox(TestImageProvider provider) + [WithFile(TestImages.Png.Filter0, CommonNonDefaultPixelTypes)] + public void DitherFilter_ShouldNotDependOnSinglePixelType(TestImageProvider provider) where TPixel : struct, IPixel { - using (Image source = provider.GetImage()) - using (Image image = source.Clone()) + using (Image image = provider.GetImage()) { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Mutate(x => x.Dither(DefaultDitherer, bounds)); + image.Mutate(x => x.Dither(DefaultDitherer)); image.DebugSave(provider); - - ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); } } [Theory] - [WithFile(TestImages.Png.CalliphoraPartial, DefaultPixelType)] - public void ApplyDiffusionFilterInBox(TestImageProvider provider) + [WithFileCollection(nameof(CommonTestImages), nameof(OrderedDitherers), PixelTypes.Rgba32)] + public void DitherFilter_WorksWithAllDitherers( + TestImageProvider provider, + IOrderedDither ditherer) where TPixel : struct, IPixel { - using (Image source = provider.GetImage()) - using (Image image = source.Clone()) + using (Image image = provider.GetImage()) { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Mutate(x => x.Diffuse(DefaultErrorDiffuser, .5F, bounds)); - image.DebugSave(provider); - - ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); + image.Mutate(x => x.Dither(ditherer)); + image.DebugSave(provider, ditherer.GetType().Name); } } } From 6285ef69eea7b388db2fb74d540bd4ad2a978656 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 4 May 2019 17:49:28 +0200 Subject: [PATCH 116/223] extended diffusion + dither tests --- .../Processors/Dithering/DitherTests.cs | 66 +++++++------------ tests/Images/External | 2 +- 2 files changed, 26 insertions(+), 42 deletions(-) diff --git a/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs index fbe7bb1822..368feefb91 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Dithering; using SixLabors.ImageSharp.Processing.Processors.Dithering; @@ -39,6 +40,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization KnownDitherers.OrderedDither3x3, KnownDitherers.BayerDither2x2 }; + private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.05f); private static IOrderedDither DefaultDitherer => KnownDitherers.BayerDither4x4; @@ -49,16 +51,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization public void ApplyDiffusionFilterInBox(TestImageProvider provider) where TPixel : struct, IPixel { - using (Image source = provider.GetImage()) - using (Image image = source.Clone()) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Mutate(x => x.Diffuse(DefaultErrorDiffuser, .5F, bounds)); - image.DebugSave(provider); - - ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); - } + provider.RunRectangleConstrainedValidatingProcessorTest( + (x, rect) => x.Diffuse(DefaultErrorDiffuser, .5F, rect), + comparer: ValidatorComparer); } [Theory] @@ -66,28 +61,19 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization public void ApplyDitherFilterInBox(TestImageProvider provider) where TPixel : struct, IPixel { - using (Image source = provider.GetImage()) - using (Image image = source.Clone()) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Mutate(x => x.Dither(DefaultDitherer, bounds)); - image.DebugSave(provider); - - ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); - } + provider.RunRectangleConstrainedValidatingProcessorTest( + (x, rect) => x.Dither(DefaultDitherer, rect), + comparer: ValidatorComparer); } [Theory] [WithFile(TestImages.Png.Filter0, CommonNonDefaultPixelTypes)] - public void DiffusionFilter_ShouldNotDependOnSinglePixelType(TestImageProvider provider) + public void DiffusionFilter_ShouldNotDependOnSinglePixelType(TestImageProvider provider) where TPixel : struct, IPixel { - using (Image image = provider.GetImage()) - { - image.Mutate(x => x.Diffuse(DefaultErrorDiffuser, 0.5f)); - image.DebugSave(provider); - } + // Increased tolerance because of compatibility issues on .NET 4.6.2: + var comparer = ImageComparer.TolerantPercentage(1f); + provider.RunValidatingProcessorTest(x => x.Diffuse(DefaultErrorDiffuser, 0.5f), comparer: comparer); } [Theory] @@ -97,11 +83,11 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization IErrorDiffuser diffuser) where TPixel : struct, IPixel { - using (Image image = provider.GetImage()) - { - image.Mutate(x => x.Diffuse(diffuser, .5F)); - image.DebugSave(provider, diffuser.GetType().Name); - } + provider.RunValidatingProcessorTest( + x => x.Diffuse(diffuser, 0.5f), + testOutputDetails: diffuser.GetType().Name, + comparer: ValidatorComparer, + appendPixelTypeToFileName: false); } [Theory] @@ -109,11 +95,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization public void DitherFilter_ShouldNotDependOnSinglePixelType(TestImageProvider provider) where TPixel : struct, IPixel { - using (Image image = provider.GetImage()) - { - image.Mutate(x => x.Dither(DefaultDitherer)); - image.DebugSave(provider); - } + provider.RunValidatingProcessorTest( + x => x.Dither(DefaultDitherer), + comparer: ValidatorComparer); } [Theory] @@ -123,11 +107,11 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization IOrderedDither ditherer) where TPixel : struct, IPixel { - using (Image image = provider.GetImage()) - { - image.Mutate(x => x.Dither(ditherer)); - image.DebugSave(provider, ditherer.GetType().Name); - } + provider.RunValidatingProcessorTest( + x => x.Dither(ditherer), + testOutputDetails: ditherer.GetType().Name, + comparer: ValidatorComparer, + appendPixelTypeToFileName: false); } } } \ No newline at end of file diff --git a/tests/Images/External b/tests/Images/External index 2a3247c6da..0c524009f4 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 2a3247c6da18b8a96cc71e1f2862ac03f2a42315 +Subproject commit 0c524009f4d4b0edefddd944defdc408c3971536 From 6015c2af6b8604fdb75665b92815ecc8ada7b2af Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sat, 4 May 2019 18:42:07 +0200 Subject: [PATCH 117/223] Add additional pixel conversion tests --- .../PixelFormats/Bgr565Tests.cs | 165 ++++++++++++++++++ .../PixelFormats/Bgra4444Tests.cs | 160 +++++++++++++++++ .../PixelFormats/Bgra5551Tests.cs | 75 +++++++- .../PixelFormats/Byte4Tests.cs | 148 +++++++++++++++- .../PixelFormats/Rgba1010102Tests.cs | 128 ++++++++++++++ 5 files changed, 672 insertions(+), 4 deletions(-) diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs index 4419fe898a..03e9054667 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; using System.Numerics; using SixLabors.ImageSharp.PixelFormats; using Xunit; @@ -9,6 +10,36 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats { public class Bgr565Tests { + /// + /// Tests the equality operators for equality. + /// + [Fact] + public void AreEqual() + { + var color1 = new Bgr565(0.0f, 0.0f, 0.0f); + var color2 = new Bgr565(new Vector3(0.0f)); + var color3 = new Bgr565(new Vector3(1.0f, 0.0f, 1.0f)); + var color4 = new Bgr565(1.0f, 0.0f, 1.0f); + + Assert.Equal(color1, color2); + Assert.Equal(color3, color4); + } + + /// + /// Tests the equality operators for inequality. + /// + [Fact] + public void AreNotEqual() + { + var color1 = new Bgr565(0.0f, 0.0f, 0.0f); + var color2 = new Bgr565(new Vector3(1.0f)); + var color3 = new Bgr565(new Vector3(1.0f, 0.0f, 0.0f)); + var color4 = new Bgr565(1.0f, 1.0f, 0.0f); + + Assert.NotEqual(color1, color2); + Assert.NotEqual(color3, color4); + } + [Fact] public void Bgr565_PackedValue() { @@ -77,6 +108,140 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(expected, bgr.PackedValue); } + [Fact] + public void Bgr565_FromArgb32() + { + // arrange + var bgr1 = default(Bgr565); + var bgr2 = default(Bgr565); + ushort expected1 = ushort.MaxValue; + ushort expected2 = ushort.MaxValue; + + // act + bgr1.FromArgb32(new Argb32(1.0f, 1.0f, 1.0f, 1.0f)); + bgr2.FromArgb32(new Argb32(1.0f, 1.0f, 1.0f, 0.0f)); + + // assert + Assert.Equal(expected1, bgr1.PackedValue); + Assert.Equal(expected2, bgr2.PackedValue); + } + + [Fact] + public void Bgr565_FromRgba32() + { + // arrange + var bgr1 = default(Bgr565); + var bgr2 = default(Bgr565); + ushort expected1 = ushort.MaxValue; + ushort expected2 = ushort.MaxValue; + + // act + bgr1.FromRgba32(new Rgba32(1.0f, 1.0f, 1.0f, 1.0f)); + bgr2.FromRgba32(new Rgba32(1.0f, 1.0f, 1.0f, 0.0f)); + + // assert + Assert.Equal(expected1, bgr1.PackedValue); + Assert.Equal(expected2, bgr2.PackedValue); + } + + [Fact] + public void Bgr565_ToRgba32() + { + // arrange + var bgra = new Bgr565(Vector3.One); + var expected = new Rgba32(Vector4.One); + var actual = default(Rgba32); + + // act + bgra.ToRgba32(ref actual); + + Assert.Equal(expected, actual); + } + + [Fact] + public void Bgra565_FromRgb48() + { + // arrange + var bgr = default(Bgr565); + ushort expectedPackedValue = ushort.MaxValue; + + // act + bgr.FromRgb48(new Rgb48(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)); + + // assert + Assert.Equal(expectedPackedValue, bgr.PackedValue); + } + + [Fact] + public void Bgra565_FromRgba64() + { + // arrange + var bgr = default(Bgr565); + ushort expectedPackedValue = ushort.MaxValue; + + // act + bgr.FromRgba64(new Rgba64(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)); + + // assert + Assert.Equal(expectedPackedValue, bgr.PackedValue); + } + + [Fact] + public void Bgr565_FromBgr24() + { + // arrange + var bgr = default(Bgr565); + ushort expected = ushort.MaxValue; + + // act + bgr.FromBgr24(new Bgr24(byte.MaxValue, byte.MaxValue, byte.MaxValue)); + + // assert + Assert.Equal(expected, bgr.PackedValue); + } + + [Fact] + public void Bgr565_FromRgb24() + { + // arrange + var bgr = default(Bgr565); + ushort expected = ushort.MaxValue; + + // act + bgr.FromRgb24(new Rgb24(byte.MaxValue, byte.MaxValue, byte.MaxValue)); + + // assert + Assert.Equal(expected, bgr.PackedValue); + } + + [Fact] + public void Bgr565_FromGrey8() + { + // arrange + var bgr = default(Bgr565); + ushort expected = ushort.MaxValue; + + // act + bgr.FromGray8(new Gray8(byte.MaxValue)); + + // assert + Assert.Equal(expected, bgr.PackedValue); + } + + [Fact] + public void Bgr565_FromGrey16() + { + // arrange + var bgr = default(Bgr565); + ushort expected = ushort.MaxValue; + + // act + bgr.FromGray16(new Gray16(ushort.MaxValue)); + + // assert + Assert.Equal(expected, bgr.PackedValue); + } + [Fact] public void Bgr565_Clamping() { diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgra4444Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgra4444Tests.cs index b6019016c8..d4c9986252 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgra4444Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgra4444Tests.cs @@ -9,6 +9,36 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats { public class Bgra4444Tests { + /// + /// Tests the equality operators for equality. + /// + [Fact] + public void AreEqual() + { + var color1 = new Bgra4444(0.0f, 0.0f, 0.0f, 0.0f); + var color2 = new Bgra4444(new Vector4(0.0f)); + var color3 = new Bgra4444(new Vector4(1.0f, 0.0f, 1.0f, 1.0f)); + var color4 = new Bgra4444(1.0f, 0.0f, 1.0f, 1.0f); + + Assert.Equal(color1, color2); + Assert.Equal(color3, color4); + } + + /// + /// Tests the equality operators for inequality. + /// + [Fact] + public void AreNotEqual() + { + var color1 = new Bgra4444(0.0f, 0.0f, 0.0f, 0.0f); + var color2 = new Bgra4444(new Vector4(1.0f)); + var color3 = new Bgra4444(new Vector4(1.0f, 0.0f, 0.0f, 1.0f)); + var color4 = new Bgra4444(1.0f, 1.0f, 0.0f, 1.0f); + + Assert.NotEqual(color1, color2); + Assert.NotEqual(color3, color4); + } + [Fact] public void Bgra4444_PackedValue() { @@ -48,6 +78,20 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(1, actual.W); } + [Fact] + public void Bgra4444_ToRgba32() + { + // arrange + var bgra = new Bgra4444(Vector4.One); + var expected = new Rgba32(Vector4.One); + var actual = default(Rgba32); + + // act + bgra.ToRgba32(ref actual); + + Assert.Equal(expected, actual); + } + [Fact] public void Bgra4444_FromScaledVector4() { @@ -78,6 +122,122 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(expected, bgra.PackedValue); } + [Fact] + public void Bgra4444_FromArgb32() + { + // arrange + var bgra = default(Bgra4444); + ushort expectedPackedValue = ushort.MaxValue; + + // act + bgra.FromArgb32(new Argb32(255, 255, 255, 255)); + + // assert + Assert.Equal(expectedPackedValue, bgra.PackedValue); + } + + [Fact] + public void Bgra4444_FromRgba32() + { + // arrange + var bgra1 = default(Bgra4444); + var bgra2 = default(Bgra4444); + ushort expectedPackedValue1 = ushort.MaxValue; + ushort expectedPackedValue2 = 0xFF0F; + + // act + bgra1.FromRgba32(new Rgba32(255, 255, 255, 255)); + bgra2.FromRgba32(new Rgba32(255, 0, 255, 255)); + + // assert + Assert.Equal(expectedPackedValue1, bgra1.PackedValue); + Assert.Equal(expectedPackedValue2, bgra2.PackedValue); + } + + [Fact] + public void Bgra4444_FromRgb48() + { + // arrange + var bgra = default(Bgra4444); + ushort expectedPackedValue = ushort.MaxValue; + + // act + bgra.FromRgb48(new Rgb48(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)); + + // assert + Assert.Equal(expectedPackedValue, bgra.PackedValue); + } + + [Fact] + public void Bgra4444_FromRgba64() + { + // arrange + var bgra = default(Bgra4444); + ushort expectedPackedValue = ushort.MaxValue; + + // act + bgra.FromRgba64(new Rgba64(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)); + + // assert + Assert.Equal(expectedPackedValue, bgra.PackedValue); + } + + [Fact] + public void Bgra4444_FromGrey16() + { + // arrange + var bgra = default(Bgra4444); + ushort expectedPackedValue = ushort.MaxValue; + + // act + bgra.FromGray16(new Gray16(ushort.MaxValue)); + + // assert + Assert.Equal(expectedPackedValue, bgra.PackedValue); + } + + [Fact] + public void Bgra4444_FromGrey8() + { + // arrange + var bgra = default(Bgra4444); + ushort expectedPackedValue = ushort.MaxValue; + + // act + bgra.FromGray8(new Gray8(byte.MaxValue)); + + // assert + Assert.Equal(expectedPackedValue, bgra.PackedValue); + } + + [Fact] + public void Bgra4444_FromBgr24() + { + // arrange + var bgra = default(Bgra4444); + ushort expectedPackedValue = ushort.MaxValue; + + // act + bgra.FromBgr24(new Bgr24(byte.MaxValue, byte.MaxValue, byte.MaxValue)); + + // assert + Assert.Equal(expectedPackedValue, bgra.PackedValue); + } + + [Fact] + public void Bgra4444_FromRgb24() + { + // arrange + var bgra = default(Bgra4444); + ushort expectedPackedValue = ushort.MaxValue; + + // act + bgra.FromRgb24(new Rgb24(byte.MaxValue, byte.MaxValue, byte.MaxValue)); + + // assert + Assert.Equal(expectedPackedValue, bgra.PackedValue); + } + [Fact] public void Bgra4444_Clamping() { diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgra5551Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgra5551Tests.cs index f5abca59a1..7751d7ab93 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgra5551Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgra5551Tests.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; using System.Numerics; using SixLabors.ImageSharp.PixelFormats; using Xunit; @@ -10,6 +9,36 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats { public class Bgra5551Tests { + /// + /// Tests the equality operators for equality. + /// + [Fact] + public void AreEqual() + { + var color1 = new Bgra5551(0.0f, 0.0f, 0.0f, 0.0f); + var color2 = new Bgra5551(new Vector4(0.0f)); + var color3 = new Bgra5551(new Vector4(1.0f, 0.0f, 0.0f, 1.0f)); + var color4 = new Bgra5551(1.0f, 0.0f, 0.0f, 1.0f); + + Assert.Equal(color1, color2); + Assert.Equal(color3, color4); + } + + /// + /// Tests the equality operators for inequality. + /// + [Fact] + public void AreNotEqual() + { + var color1 = new Bgra5551(0.0f, 0.0f, 0.0f, 0.0f); + var color2 = new Bgra5551(new Vector4(1.0f)); + var color3 = new Bgra5551(new Vector4(1.0f, 0.0f, 0.0f, 1.0f)); + var color4 = new Bgra5551(1.0f, 1.0f, 0.0f, 1.0f); + + Assert.NotEqual(color1, color2); + Assert.NotEqual(color3, color4); + } + [Fact] public void Bgra5551_PackedValue() { @@ -54,6 +83,20 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(1, actual.W); } + [Fact] + public void Bgra5551_ToRgba32() + { + // arrange + var bgra = new Bgra5551(Vector4.One); + var expected = new Rgba32(Vector4.One); + var actual = default(Rgba32); + + // act + bgra.ToRgba32(ref actual); + + Assert.Equal(expected, actual); + } + [Fact] public void Bgra5551_FromScaledVector4() { @@ -70,6 +113,22 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(expected, actual); } + [Fact] + public void Bgra5551_FromBgra5551() + { + // arrange + var bgra = default(Bgra5551); + var actual = default(Bgra5551); + var expected = new Bgra5551(1.0f, 0.0f, 1.0f, 1.0f); + + // act + bgra.FromBgra5551(expected); + actual.FromBgra5551(bgra); + + // assert + Assert.Equal(expected, actual); + } + [Fact] public void Bgra5551_FromRgba32() { @@ -120,6 +179,20 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(expectedPackedValue, bgra.PackedValue); } + [Fact] + public void Bgra5551_FromRgb48() + { + // arrange + var bgra = default(Bgra5551); + ushort expectedPackedValue = ushort.MaxValue; + + // act + bgra.FromRgb48(new Rgb48(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)); + + // assert + Assert.Equal(expectedPackedValue, bgra.PackedValue); + } + [Fact] public void Bgra5551_FromRgba64() { diff --git a/tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs index 9174a6abdb..ce9e56317e 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs @@ -9,6 +9,36 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats { public class Byte4Tests { + /// + /// Tests the equality operators for equality. + /// + [Fact] + public void AreEqual() + { + var color1 = new Byte4(0.0f, 0.0f, 0.0f, 0.0f); + var color2 = new Byte4(new Vector4(0.0f)); + var color3 = new Byte4(new Vector4(1.0f, 0.0f, 1.0f, 1.0f)); + var color4 = new Byte4(1.0f, 0.0f, 1.0f, 1.0f); + + Assert.Equal(color1, color2); + Assert.Equal(color3, color4); + } + + /// + /// Tests the equality operators for inequality. + /// + [Fact] + public void AreNotEqual() + { + var color1 = new Byte4(0.0f, 0.0f, 0.0f, 0.0f); + var color2 = new Byte4(new Vector4(1.0f)); + var color3 = new Byte4(new Vector4(1.0f, 0.0f, 0.0f, 1.0f)); + var color4 = new Byte4(1.0f, 1.0f, 0.0f, 1.0f); + + Assert.NotEqual(color1, color2); + Assert.NotEqual(color3, color4); + } + [Fact] public void Byte4_PackedValue() { @@ -61,18 +91,130 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(expected, actual); } + [Fact] + public void Byte4_FromArgb32() + { + // arrange + var byte4 = default(Byte4); + uint expectedPackedValue = uint.MaxValue; + + // act + byte4.FromArgb32(new Argb32(255, 255, 255, 255)); + + // assert + Assert.Equal(expectedPackedValue, byte4.PackedValue); + } + + [Fact] + public void Byte4_FromBgr24() + { + // arrange + var byte4 = default(Byte4); + uint expectedPackedValue = uint.MaxValue; + + // act + byte4.FromBgr24(new Bgr24(byte.MaxValue, byte.MaxValue, byte.MaxValue)); + + // assert + Assert.Equal(expectedPackedValue, byte4.PackedValue); + } + + [Fact] + public void Byte4_FromGrey8() + { + // arrange + var byte4 = default(Byte4); + uint expectedPackedValue = uint.MaxValue; + + // act + byte4.FromGray8(new Gray8(byte.MaxValue)); + + // assert + Assert.Equal(expectedPackedValue, byte4.PackedValue); + } + + [Fact] + public void Byte4_FromGrey16() + { + // arrange + var byte4 = default(Byte4); + uint expectedPackedValue = uint.MaxValue; + + // act + byte4.FromGray16(new Gray16(ushort.MaxValue)); + + // assert + Assert.Equal(expectedPackedValue, byte4.PackedValue); + } + + [Fact] + public void Byte4_FromRgb24() + { + // arrange + var byte4 = default(Byte4); + uint expectedPackedValue = uint.MaxValue; + + // act + byte4.FromRgb24(new Rgb24(byte.MaxValue, byte.MaxValue, byte.MaxValue)); + + // assert + Assert.Equal(expectedPackedValue, byte4.PackedValue); + } + [Fact] public void Byte4_FromBgra5551() { // arrange - var rgb = default(Byte4); + var byte4 = default(Byte4); uint expected = 0xFFFFFFFF; // act - rgb.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + byte4.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + + // assert + Assert.Equal(expected, byte4.PackedValue); + } + + [Fact] + public void Byte4_FromRgba32() + { + // arrange + var byte4 = default(Byte4); + uint expectedPackedValue1 = uint.MaxValue; + + // act + byte4.FromRgba32(new Rgba32(255, 255, 255, 255)); + + // assert + Assert.Equal(expectedPackedValue1, byte4.PackedValue); + } + + [Fact] + public void Byte4_FromRgb48() + { + // arrange + var byte4 = default(Byte4); + uint expectedPackedValue = uint.MaxValue; + + // act + byte4.FromRgb48(new Rgb48(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)); + + // assert + Assert.Equal(expectedPackedValue, byte4.PackedValue); + } + + [Fact] + public void Byte4_FromRgba64() + { + // arrange + var byte4 = default(Byte4); + uint expectedPackedValue = uint.MaxValue; + + // act + byte4.FromRgba64(new Rgba64(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)); // assert - Assert.Equal(expected, rgb.PackedValue); + Assert.Equal(expectedPackedValue, byte4.PackedValue); } [Fact] diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs index 0dbed2d2cb..19a5d4b22e 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs @@ -9,6 +9,36 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats { public class Rgba1010102Tests { + /// + /// Tests the equality operators for equality. + /// + [Fact] + public void AreEqual() + { + var color1 = new Rgba1010102(0.0f, 0.0f, 0.0f, 0.0f); + var color2 = new Rgba1010102(new Vector4(0.0f)); + var color3 = new Rgba1010102(new Vector4(1.0f, 0.0f, 1.0f, 1.0f)); + var color4 = new Rgba1010102(1.0f, 0.0f, 1.0f, 1.0f); + + Assert.Equal(color1, color2); + Assert.Equal(color3, color4); + } + + /// + /// Tests the equality operators for inequality. + /// + [Fact] + public void AreNotEqual() + { + var color1 = new Rgba1010102(0.0f, 0.0f, 0.0f, 0.0f); + var color2 = new Rgba1010102(new Vector4(1.0f)); + var color3 = new Rgba1010102(new Vector4(1.0f, 0.0f, 0.0f, 1.0f)); + var color4 = new Rgba1010102(1.0f, 1.0f, 0.0f, 1.0f); + + Assert.NotEqual(color1, color2); + Assert.NotEqual(color3, color4); + } + [Fact] public void Rgba1010102_PackedValue() { @@ -78,6 +108,104 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(expected, rgba.PackedValue); } + [Fact] + public void Rgba1010102_FromArgb32() + { + // arrange + var rgba = default(Rgba1010102); + uint expectedPackedValue = uint.MaxValue; + + // act + rgba.FromArgb32(new Argb32(255, 255, 255, 255)); + + // assert + Assert.Equal(expectedPackedValue, rgba.PackedValue); + } + + [Fact] + public void Rgba1010102_FromBgr24() + { + // arrange + var rgba = default(Rgba1010102); + uint expectedPackedValue = uint.MaxValue; + + // act + rgba.FromBgr24(new Bgr24(byte.MaxValue, byte.MaxValue, byte.MaxValue)); + + // assert + Assert.Equal(expectedPackedValue, rgba.PackedValue); + } + + [Fact] + public void Rgba1010102_FromGrey8() + { + // arrange + var rgba = default(Rgba1010102); + uint expectedPackedValue = uint.MaxValue; + + // act + rgba.FromGray8(new Gray8(byte.MaxValue)); + + // assert + Assert.Equal(expectedPackedValue, rgba.PackedValue); + } + + [Fact] + public void Rgba1010102_FromGrey16() + { + // arrange + var rgba = default(Rgba1010102); + uint expectedPackedValue = uint.MaxValue; + + // act + rgba.FromGray16(new Gray16(ushort.MaxValue)); + + // assert + Assert.Equal(expectedPackedValue, rgba.PackedValue); + } + + [Fact] + public void Rgba1010102_FromRgb24() + { + // arrange + var rgba = default(Rgba1010102); + uint expectedPackedValue = uint.MaxValue; + + // act + rgba.FromRgb24(new Rgb24(byte.MaxValue, byte.MaxValue, byte.MaxValue)); + + // assert + Assert.Equal(expectedPackedValue, rgba.PackedValue); + } + + [Fact] + public void Rgba1010102_FromRgb48() + { + // arrange + var rgba = default(Rgba1010102); + uint expectedPackedValue = uint.MaxValue; + + // act + rgba.FromRgb48(new Rgb48(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)); + + // assert + Assert.Equal(expectedPackedValue, rgba.PackedValue); + } + + [Fact] + public void Rgba1010102_FromRgba64() + { + // arrange + var rgba = default(Rgba1010102); + uint expectedPackedValue = uint.MaxValue; + + // act + rgba.FromRgba64(new Rgba64(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)); + + // assert + Assert.Equal(expectedPackedValue, rgba.PackedValue); + } + [Fact] public void Rgba1010102_Clamping() { From a96423ddc5ecbb0859fa5059b1e2fee9e748246d Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 4 May 2019 19:05:08 +0200 Subject: [PATCH 118/223] validating tests for Effects --- .../Processors/Effects/BackgroundColorTest.cs | 35 ++++------ .../Processors/Effects/OilPaintTest.cs | 42 +++++------ .../Processors/Effects/PixelateTest.cs | 70 +++---------------- tests/Images/External | 2 +- 4 files changed, 47 insertions(+), 102 deletions(-) diff --git a/tests/ImageSharp.Tests/Processing/Processors/Effects/BackgroundColorTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Effects/BackgroundColorTest.cs index 792c7b0802..d7f77c9565 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Effects/BackgroundColorTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Effects/BackgroundColorTest.cs @@ -10,35 +10,30 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects { - public class BackgroundColorTest : FileTestBase + [GroupOutput("Effects")] + public class BackgroundColorTest { + public static readonly string[] InputImages = + { + TestImages.Png.Splash, + TestImages.Png.Ducky + }; + [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyBackgroundColorFilter(TestImageProvider provider) + [WithFileCollection(nameof(InputImages), PixelTypes.Rgba32)] + public void FullImage(TestImageProvider provider) where TPixel : struct, IPixel { - using (Image image = provider.GetImage()) - { - image.Mutate(x => x.BackgroundColor(NamedColors.HotPink)); - image.DebugSave(provider); - } + provider.RunValidatingProcessorTest(x => x.BackgroundColor(NamedColors.HotPink)); } [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyBackgroundColorFilterInBox(TestImageProvider provider) + [WithFileCollection(nameof(InputImages), PixelTypes.Rgba32)] + public void InBox(TestImageProvider provider) where TPixel : struct, IPixel { - using (Image source = provider.GetImage()) - using (var image = source.Clone()) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Mutate(x => x.BackgroundColor(NamedColors.HotPink, bounds)); - image.DebugSave(provider); - - ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); - } + provider.RunRectangleConstrainedValidatingProcessorTest( + (x, rect) => x.BackgroundColor(NamedColors.HotPink, rect)); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Effects/OilPaintTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Effects/OilPaintTest.cs index d4429aaf3d..ea2273cd5d 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Effects/OilPaintTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Effects/OilPaintTest.cs @@ -10,40 +10,40 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects { - public class OilPaintTest : FileTestBase + [GroupOutput("Effects")] + public class OilPaintTest { public static readonly TheoryData OilPaintValues = new TheoryData { - { 15, 10 }, { 6, 5 } + { 15, 10 }, + { 6, 5 } }; + public static readonly string[] InputImages = + { + TestImages.Png.CalliphoraPartial, + TestImages.Bmp.Car + }; [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(OilPaintValues), DefaultPixelType)] - public void ApplyOilPaintFilter(TestImageProvider provider, int levels, int brushSize) + [WithFileCollection(nameof(InputImages), nameof(OilPaintValues), PixelTypes.Rgba32)] + public void FullImage(TestImageProvider provider, int levels, int brushSize) where TPixel : struct, IPixel { - using (Image image = provider.GetImage()) - { - image.Mutate(x => x.OilPaint(levels, brushSize)); - image.DebugSave(provider, string.Join("-", levels, brushSize)); - } + provider.RunValidatingProcessorTest( + x => x.OilPaint(levels, brushSize), + $"{levels}-{brushSize}", + appendPixelTypeToFileName: false); } [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(OilPaintValues), DefaultPixelType)] - public void ApplyOilPaintFilterInBox(TestImageProvider provider, int levels, int brushSize) + [WithFileCollection(nameof(InputImages), nameof(OilPaintValues), PixelTypes.Rgba32)] + [WithTestPatternImages(nameof(OilPaintValues), 100, 100, PixelTypes.Rgba32)] + public void InBox(TestImageProvider provider, int levels, int brushSize) where TPixel : struct, IPixel { - using (Image source = provider.GetImage()) - using (Image image = source.Clone()) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Mutate(x => x.OilPaint(levels, brushSize, bounds)); - image.DebugSave(provider, string.Join("-", levels, brushSize)); - - ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); - } + provider.RunRectangleConstrainedValidatingProcessorTest( + (x, rect) => x.OilPaint(levels, brushSize, rect), + $"{levels}-{brushSize}"); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Effects/PixelateTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Effects/PixelateTest.cs index cb9a0ba0cf..726f4b707b 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Effects/PixelateTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Effects/PixelateTest.cs @@ -4,82 +4,32 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; - using SixLabors.Primitives; + using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects { - public class PixelateTest : FileTestBase + [GroupOutput("Effects")] + public class PixelateTest { - public static readonly TheoryData PixelateValues - = new TheoryData - { - 4 , - 8 - }; + public static readonly TheoryData PixelateValues = new TheoryData { 4, 8 }; [Theory] - [WithTestPatternImages(nameof(PixelateValues), 320, 240, PixelTypes.Rgba32)] - public void ImageShouldApplyPixelateFilter(TestImageProvider provider, int value) + [WithFile(TestImages.Png.Ducky, nameof(PixelateValues), PixelTypes.Rgba32)] + public void FullImage(TestImageProvider provider, int value) where TPixel : struct, IPixel { - using (Image image = provider.GetImage()) - { - image.Mutate(x => x.Pixelate(value)); - image.DebugSave(provider, value); - - // Test the neigbouring pixels - for (int y = 0; y < image.Height; y += value) - { - for (int x = 0; x < image.Width; x += value) - { - TPixel source = image[x, y]; - for (int pixY = y; pixY < y + value && pixY < image.Height; pixY++) - { - for (int pixX = x; pixX < x + value && pixX < image.Width; pixX++) - { - Assert.Equal(source, image[pixX, pixY]); - } - } - } - } - } + provider.RunValidatingProcessorTest(x => x.Pixelate(value), value, appendPixelTypeToFileName: false); } [Theory] [WithTestPatternImages(nameof(PixelateValues), 320, 240, PixelTypes.Rgba32)] - public void ImageShouldApplyPixelateFilterInBox(TestImageProvider provider, int value) + [WithFile(TestImages.Png.CalliphoraPartial, nameof(PixelateValues), PixelTypes.Rgba32)] + public void InBox(TestImageProvider provider, int value) where TPixel : struct, IPixel { - using (Image source = provider.GetImage()) - using (var image = source.Clone()) - { - var bounds = new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2); - - image.Mutate(x => x.Pixelate(value, bounds)); - image.DebugSave(provider, value); - - for (int y = 0; y < image.Height; y++) - { - for (int x = 0; x < image.Width; x++) - { - int tx = x; - int ty = y; - TPixel sourceColor = source[tx, ty]; - if (bounds.Contains(tx, ty)) - { - int sourceX = tx - ((tx - bounds.Left) % value) + (value / 2); - int sourceY = ty - ((ty - bounds.Top) % value) + (value / 2); - - sourceColor = image[sourceX, sourceY]; - } - Assert.Equal(sourceColor, image[tx, ty]); - } - } - - ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); - } + provider.RunRectangleConstrainedValidatingProcessorTest((x, rect) => x.Pixelate(value, rect), value); } } } \ No newline at end of file diff --git a/tests/Images/External b/tests/Images/External index 0c524009f4..50de3b9869 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 0c524009f4d4b0edefddd944defdc408c3971536 +Subproject commit 50de3b98694f20c9307d4d7cdac870f5820823c8 From 316c87a1705b877b5e67450c24d1fa1e7e2413bf Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 4 May 2019 19:42:44 +0200 Subject: [PATCH 119/223] validating tests for AutoOrient --- .../Processors/Transforms/AutoOrientTests.cs | 58 +++++++++++-------- tests/Images/External | 2 +- 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/AutoOrientTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/AutoOrientTests.cs index 28b01061ea..6dc9b36309 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/AutoOrientTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/AutoOrientTests.cs @@ -6,27 +6,16 @@ using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms { - public class AutoOrientTests : FileTestBase + [GroupOutput("Transforms")] + public class AutoOrientTests { - public static readonly string[] FlipFiles = { TestImages.Bmp.F }; - - public static readonly TheoryData OrientationValues - = new TheoryData - { - { RotateMode.None, FlipMode.None, 0 }, - { RotateMode.None, FlipMode.None, 1 }, - { RotateMode.None, FlipMode.Horizontal, 2 }, - { RotateMode.Rotate180, FlipMode.None, 3 }, - { RotateMode.Rotate180, FlipMode.Horizontal, 4 }, - { RotateMode.Rotate90, FlipMode.Horizontal, 5 }, - { RotateMode.Rotate270, FlipMode.None, 6 }, - { RotateMode.Rotate90, FlipMode.Vertical, 7 }, - { RotateMode.Rotate90, FlipMode.None, 8 }, - }; + public const string FlipTestFile = TestImages.Bmp.F; public static readonly TheoryData InvalidOrientationValues = new TheoryData @@ -38,27 +27,38 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms { ExifDataType.SignedLong, BitConverter.GetBytes((int) 5) } }; + public static readonly TheoryData ExifOrientationValues = new TheoryData() + { + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8 + }; + [Theory] - [WithFileCollection(nameof(FlipFiles), nameof(OrientationValues), DefaultPixelType)] - public void ImageShouldAutoRotate(TestImageProvider provider, RotateMode rotateType, FlipMode flipType, ushort orientation) + [WithFile(FlipTestFile, nameof(ExifOrientationValues), PixelTypes.Rgba32)] + public void AutoOrient_WorksForAllExifOrientations(TestImageProvider provider, ushort orientation) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) { image.Metadata.ExifProfile = new ExifProfile(); image.Metadata.ExifProfile.SetValue(ExifTag.Orientation, orientation); - - image.Mutate(x => x.RotateFlip(rotateType, flipType)); - image.DebugSave(provider, string.Join("_", rotateType, flipType, orientation, "1_before")); - + image.Mutate(x => x.AutoOrient()); - image.DebugSave(provider, string.Join("_", rotateType, flipType, orientation, "2_after")); + image.DebugSave(provider, orientation, appendPixelTypeToFileName: false); + image.CompareToReferenceOutput(provider, orientation, appendPixelTypeToFileName: false); } } [Theory] - [WithFileCollection(nameof(FlipFiles), nameof(InvalidOrientationValues), DefaultPixelType)] - public void ImageShouldAutoRotateInvalidValues(TestImageProvider provider, ExifDataType dataType, byte[] orientation) + [WithFile(FlipTestFile, nameof(InvalidOrientationValues), PixelTypes.Rgba32)] + public void AutoOrient_WorksWithCorruptExifData(TestImageProvider provider, ExifDataType dataType, byte[] orientation) where TPixel : struct, IPixel { var profile = new ExifProfile(); @@ -72,11 +72,19 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms bytes[18] = (byte)dataType; // Change the number of components bytes[20] = 1; + + byte[] orientationCodeData = new byte[8]; + Array.Copy(orientation, orientationCodeData, orientation.Length); + + ulong orientationCode = BitConverter.ToUInt64(orientationCodeData, 0); using (Image image = provider.GetImage()) + using (Image reference = image.Clone()) { image.Metadata.ExifProfile = new ExifProfile(bytes); image.Mutate(x => x.AutoOrient()); + image.DebugSave(provider, $"{dataType}-{orientationCode}", appendPixelTypeToFileName: false); + ImageComparer.Exact.VerifySimilarity(image, reference); } } } diff --git a/tests/Images/External b/tests/Images/External index 50de3b9869..497dda8717 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 50de3b98694f20c9307d4d7cdac870f5820823c8 +Subproject commit 497dda87179af1dc323064140765f72b892116a3 From 8c2344794b49da56af5a58c9cba5053088656f7c Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 4 May 2019 20:22:06 +0200 Subject: [PATCH 120/223] validating tests for Skew and Entropy crop --- .../Processors/Transforms/CropTest.cs | 2 +- .../Processors/Transforms/EntropyCropTest.cs | 27 ++++--- .../Processors/Transforms/SkewTest.cs | 72 ------------------- .../Processors/Transforms/SkewTests.cs | 67 +++++++++++++++++ tests/Images/External | 2 +- 5 files changed, 82 insertions(+), 88 deletions(-) delete mode 100644 tests/ImageSharp.Tests/Processing/Processors/Transforms/SkewTest.cs create mode 100644 tests/ImageSharp.Tests/Processing/Processors/Transforms/SkewTests.cs diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/CropTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/CropTest.cs index c01c3b1bd3..50217e892d 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/CropTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/CropTest.cs @@ -13,7 +13,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms { [GroupOutput("Transforms")] - public class CropTest : FileTestBase + public class CropTest { [Theory] [WithTestPatternImages(70, 30, PixelTypes.Rgba32, 0, 0, 70, 30)] diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/EntropyCropTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/EntropyCropTest.cs index 7285270217..d20e6fa359 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/EntropyCropTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/EntropyCropTest.cs @@ -7,25 +7,24 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms { - public class EntropyCropTest : FileTestBase + [GroupOutput("Transforms")] + public class EntropyCropTest { - public static readonly TheoryData EntropyCropValues - = new TheoryData - { - .25F, - .75F - }; + public static readonly TheoryData EntropyCropValues = new TheoryData { .25F, .75F }; + + public static readonly string[] InputImages = + { + TestImages.Png.Ducky, + TestImages.Jpeg.Baseline.Jpeg400, + TestImages.Jpeg.Baseline.MultiScanBaselineCMYK + }; [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(EntropyCropValues), DefaultPixelType)] - public void ImageShouldEntropyCrop(TestImageProvider provider, float value) + [WithFileCollection(nameof(InputImages), nameof(EntropyCropValues), PixelTypes.Rgba32)] + public void EntropyCrop(TestImageProvider provider, float value) where TPixel : struct, IPixel { - using (Image image = provider.GetImage()) - { - image.Mutate(x => x.EntropyCrop(value)); - image.DebugSave(provider, value); - } + provider.RunValidatingProcessorTest(x => x.EntropyCrop(value), value, appendPixelTypeToFileName: false); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/SkewTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/SkewTest.cs deleted file mode 100644 index 29c51543fc..0000000000 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/SkewTest.cs +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Collections.Generic; -using System.Reflection; - -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors.Transforms; -using SixLabors.ImageSharp.PixelFormats; -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms -{ - public class SkewTest : FileTestBase - { - public static readonly TheoryData SkewValues - = new TheoryData - { - { 20, 10 }, - { -20, -10 } - }; - - public static readonly List ResamplerNames - = new List - { - nameof(KnownResamplers.Bicubic), - nameof(KnownResamplers.Box), - nameof(KnownResamplers.CatmullRom), - nameof(KnownResamplers.Hermite), - nameof(KnownResamplers.Lanczos2), - nameof(KnownResamplers.Lanczos3), - nameof(KnownResamplers.Lanczos5), - nameof(KnownResamplers.Lanczos8), - nameof(KnownResamplers.MitchellNetravali), - nameof(KnownResamplers.NearestNeighbor), - nameof(KnownResamplers.Robidoux), - nameof(KnownResamplers.RobidouxSharp), - nameof(KnownResamplers.Spline), - nameof(KnownResamplers.Triangle), - nameof(KnownResamplers.Welch), - }; - - [Theory] - [WithTestPatternImages(nameof(SkewValues), 100, 50, DefaultPixelType)] - public void ImageShouldSkew(TestImageProvider provider, float x, float y) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Mutate(i => i.Skew(x, y)); - image.DebugSave(provider, string.Join("_", x, y)); - } - } - - [Theory] - [WithTestPatternImages(nameof(SkewValues), 100, 50, DefaultPixelType)] - public void ImageShouldSkewWithSampler(TestImageProvider provider, float x, float y) - where TPixel : struct, IPixel - { - foreach (string resamplerName in ResamplerNames) - { - IResampler sampler = TestUtils.GetResampler(resamplerName); - using (Image image = provider.GetImage()) - { - image.Mutate(i => i.Skew(x, y, sampler)); - image.DebugSave(provider, string.Join("_", x, y, resamplerName)); - } - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/SkewTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/SkewTests.cs new file mode 100644 index 0000000000..ad77027f0f --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/SkewTests.cs @@ -0,0 +1,67 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Transforms; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms +{ + [GroupOutput("Transforms")] + public class SkewTests + { + private const PixelTypes CommonPixelTypes = PixelTypes.Bgra32 | PixelTypes.Rgb24; + + public static readonly string[] ResamplerNames = new[] + { + nameof(KnownResamplers.Bicubic), + nameof(KnownResamplers.Box), + nameof(KnownResamplers.CatmullRom), + nameof(KnownResamplers.Hermite), + nameof(KnownResamplers.Lanczos2), + nameof(KnownResamplers.Lanczos3), + nameof(KnownResamplers.Lanczos5), + nameof(KnownResamplers.Lanczos8), + nameof(KnownResamplers.MitchellNetravali), + nameof(KnownResamplers.NearestNeighbor), + nameof(KnownResamplers.Robidoux), + nameof(KnownResamplers.RobidouxSharp), + nameof(KnownResamplers.Spline), + nameof(KnownResamplers.Triangle), + nameof(KnownResamplers.Welch), + }; + + public static readonly TheoryData SkewValues = new TheoryData + { + { 20, 10 }, + { -20, -10 } + }; + + private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.01f); + + [Theory] + [WithTestPatternImages(nameof(SkewValues), 100, 50, CommonPixelTypes)] + public void Skew_IsNotBoundToSinglePixelType(TestImageProvider provider, float x, float y) + where TPixel : struct, IPixel + { + provider.RunValidatingProcessorTest(ctx => ctx.Skew(x, y), $"{x}_{y}", ValidatorComparer); + } + + [Theory] + [WithFile(TestImages.Png.Ducky, nameof(ResamplerNames), PixelTypes.Rgba32)] + public void Skew_WorksWithAllResamplers(TestImageProvider provider, string resamplerName) + where TPixel : struct, IPixel + { + IResampler sampler = TestUtils.GetResampler(resamplerName); + + provider.RunValidatingProcessorTest( + x => x.Skew(21, 32, sampler), + resamplerName, + comparer: ValidatorComparer, + appendPixelTypeToFileName: false); + } + } +} \ No newline at end of file diff --git a/tests/Images/External b/tests/Images/External index 497dda8717..56ee5df145 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 497dda87179af1dc323064140765f72b892116a3 +Subproject commit 56ee5df145d9696e401e2bc53e615c553cfb32d5 From e9ce809e8d60a3e688cef28a4e0ddda1beb8126a Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 4 May 2019 20:57:30 +0200 Subject: [PATCH 121/223] validating tests for Overlay processors --- .../Processors/Overlays/GlowTest.cs | 57 ++------------- .../Processors/Overlays/OverlayTestBase.cs | 70 +++++++++++++++++++ .../Processors/Overlays/VignetteTest.cs | 61 ++-------------- tests/Images/External | 2 +- 4 files changed, 83 insertions(+), 107 deletions(-) create mode 100644 tests/ImageSharp.Tests/Processing/Processors/Overlays/OverlayTestBase.cs diff --git a/tests/ImageSharp.Tests/Processing/Processors/Overlays/GlowTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Overlays/GlowTest.cs index 479ee346a5..113c13b8ae 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Overlays/GlowTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Overlays/GlowTest.cs @@ -10,59 +10,14 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Overlays { - public class GlowTest : FileTestBase + [GroupOutput("Overlays")] + public class GlowTest : OverlayTestBase { - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyGlowFilter(TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Mutate(x => x.Glow()); - image.DebugSave(provider); - } - } + protected override void Apply(IImageProcessingContext ctx, T color) => ctx.Glow(color); - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyGlowFilterColor(TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Mutate(x => x.Glow(NamedColors.Orange)); - image.DebugSave(provider); - } - } + protected override void Apply(IImageProcessingContext ctx, float radiusX, float radiusY) => + ctx.Glow(radiusX); - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyGlowFilterRadius(TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Mutate(x => x.Glow(image.Width / 4F)); - image.DebugSave(provider); - } - } - - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyGlowFilterInBox(TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image source = provider.GetImage()) - using (var image = source.Clone()) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Mutate(x => x.Glow(bounds)); - image.DebugSave(provider); - - ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); - } - } + protected override void Apply(IImageProcessingContext ctx, Rectangle rect) => ctx.Glow(rect); } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Overlays/OverlayTestBase.cs b/tests/ImageSharp.Tests/Processing/Processors/Overlays/OverlayTestBase.cs new file mode 100644 index 0000000000..d2abcd7312 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Overlays/OverlayTestBase.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Reflection; + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; +using SixLabors.Primitives; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Overlays +{ + [GroupOutput("Overlays")] + public abstract class OverlayTestBase + { + public static string[] ColorNames = { "Blue", "White" }; + + public static string[] InputImages = { TestImages.Png.Ducky, TestImages.Png.Splash }; + + private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.05f); + + [Theory] + [WithFileCollection(nameof(InputImages), nameof(ColorNames), PixelTypes.Rgba32)] + public void FullImage_ApplyColor(TestImageProvider provider, string colorName) + where TPixel : struct, IPixel + { + provider.Utility.TestGroupName = this.GetType().Name; + var f = (FieldInfo)typeof(NamedColors).GetMember(colorName)[0]; + TPixel color = (TPixel)f.GetValue(null); + + provider.RunValidatingProcessorTest(x => this.Apply(x, color), colorName, ValidatorComparer, appendPixelTypeToFileName: false); + } + + [Theory] + [WithFileCollection(nameof(InputImages), PixelTypes.Rgba32)] + public void FullImage_ApplyRadius(TestImageProvider provider) + where TPixel : struct, IPixel + { + provider.Utility.TestGroupName = this.GetType().Name; + provider.RunValidatingProcessorTest( + x => + { + Size size = x.GetCurrentSize(); + this.Apply(x, size.Width / 4f, size.Height / 4f); + }, + comparer: ValidatorComparer, + appendPixelTypeToFileName: false); + } + + [Theory] + [WithFileCollection(nameof(InputImages), PixelTypes.Rgba32)] + public void InBox(TestImageProvider provider) + where TPixel : struct, IPixel + { + provider.Utility.TestGroupName = this.GetType().Name; + provider.RunRectangleConstrainedValidatingProcessorTest((x, rect) => this.Apply(x, rect)); + } + + protected abstract void Apply(IImageProcessingContext ctx, T color) + where T : struct, IPixel; + + protected abstract void Apply(IImageProcessingContext ctx, float radiusX, float radiusY) + where T : struct, IPixel; + + protected abstract void Apply(IImageProcessingContext ctx, Rectangle rect) + where T : struct, IPixel; + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Overlays/VignetteTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Overlays/VignetteTest.cs index 3a378a0951..ad04a827db 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Overlays/VignetteTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Overlays/VignetteTest.cs @@ -1,68 +1,19 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; - using SixLabors.Primitives; -using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Overlays { - public class VignetteTest : FileTestBase + [GroupOutput("Overlays")] + public class VignetteTest : OverlayTestBase { - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyVignetteFilter(TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Mutate(x => x.Vignette()); - image.DebugSave(provider); - } - } - - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyVignetteFilterColor(TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Mutate(x => x.Vignette(NamedColors.Orange)); - image.DebugSave(provider); - } - } - - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyVignetteFilterRadius(TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Mutate(x => x.Vignette(image.Width / 4F, image.Height / 4F)); - image.DebugSave(provider); - } - } - - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyVignetteFilterInBox(TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image source = provider.GetImage()) - using (var image = source.Clone()) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + protected override void Apply(IImageProcessingContext ctx, T color) => ctx.Vignette(color); - image.Mutate(x => x.Vignette(bounds)); - image.DebugSave(provider); + protected override void Apply(IImageProcessingContext ctx, float radiusX, float radiusY) => + ctx.Vignette(radiusX, radiusY); - ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); - } - } + protected override void Apply(IImageProcessingContext ctx, Rectangle rect) => ctx.Vignette(rect); } } \ No newline at end of file diff --git a/tests/Images/External b/tests/Images/External index 56ee5df145..854ba68b05 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 56ee5df145d9696e401e2bc53e615c553cfb32d5 +Subproject commit 854ba68b05162bdd87c0c9bbdcdb1c848aa6ddbe From 65dc0ab53a6e053768089806b87638fe6504d26f Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sat, 4 May 2019 21:05:25 +0200 Subject: [PATCH 122/223] Add more pixel conversion tests --- .../PixelImplementations/Byte4.cs | 2 +- .../PixelFormats/Argb32Tests.cs | 62 +++++++ .../PixelFormats/Bgr24Tests.cs | 18 ++ .../PixelFormats/Bgr565Tests.cs | 1 - .../PixelFormats/Bgra32Tests.cs | 25 +++ .../PixelFormats/Byte4Tests.cs | 14 ++ .../PixelFormats/Gray16Tests.cs | 18 ++ .../PixelFormats/Gray8Tests.cs | 20 ++- .../PixelFormats/NormalizedByte4Tests.cs | 156 ++++++++++++++++++ .../PixelFormats/NormalizedShort4Tests.cs | 156 ++++++++++++++++++ .../PixelFormats/Rgba1010102Tests.cs | 18 ++ .../PixelFormats/RgbaVectorTests.cs | 42 +++++ 12 files changed, 528 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs index a07cd5213c..ab8a13c166 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs @@ -149,7 +149,7 @@ namespace SixLabors.ImageSharp.PixelFormats public override string ToString() { var vector = this.ToVector4(); - return FormattableString.Invariant($"Bgra5551({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##}, {vector.W:#0.##})"); + return FormattableString.Invariant($"Byte4({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##}, {vector.W:#0.##})"); } /// diff --git a/tests/ImageSharp.Tests/PixelFormats/Argb32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Argb32Tests.cs index 4f3394e691..1ccf485fe0 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Argb32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Argb32Tests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; using System.Numerics; using SixLabors.ImageSharp.PixelFormats; using Xunit; @@ -9,6 +10,67 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats { public class Argb32Tests { + /// + /// Tests the equality operators for equality. + /// + [Fact] + public void AreEqual() + { + var color1 = new Argb32(0.0f, 0.0f, 0.0f, 0.0f); + var color2 = new Argb32(new Vector4(0.0f)); + var color3 = new Argb32(new Vector4(1.0f, 0.0f, 1.0f, 1.0f)); + var color4 = new Argb32(1.0f, 0.0f, 1.0f, 1.0f); + + Assert.Equal(color1, color2); + Assert.Equal(color3, color4); + } + + /// + /// Tests the equality operators for inequality. + /// + [Fact] + public void AreNotEqual() + { + var color1 = new Argb32(0.0f, 0.0f, 0.0f, 0.0f); + var color2 = new Argb32(new Vector4(1.0f)); + var color3 = new Argb32(new Vector4(1.0f, 0.0f, 0.0f, 1.0f)); + var color4 = new Argb32(1.0f, 1.0f, 0.0f, 1.0f); + + Assert.NotEqual(color1, color2); + Assert.NotEqual(color3, color4); + } + + /// + /// Tests whether the color constructor correctly assign properties. + /// + [Fact] + public void ConstructorAssignsProperties() + { + var color1 = new Argb32(1, .1f, .133f, .864f); + Assert.Equal(255, color1.R); + Assert.Equal((byte)Math.Round(.1f * 255), color1.G); + Assert.Equal((byte)Math.Round(.133f * 255), color1.B); + Assert.Equal((byte)Math.Round(.864f * 255), color1.A); + + var color2 = new Argb32(1, .1f, .133f); + Assert.Equal(255, color2.R); + Assert.Equal(Math.Round(.1f * 255), color2.G); + Assert.Equal(Math.Round(.133f * 255), color2.B); + Assert.Equal(255, color2.A); + + var color4 = new Argb32(new Vector3(1, .1f, .133f)); + Assert.Equal(255, color4.R); + Assert.Equal(Math.Round(.1f * 255), color4.G); + Assert.Equal(Math.Round(.133f * 255), color4.B); + Assert.Equal(255, color4.A); + + var color5 = new Argb32(new Vector4(1, .1f, .133f, .5f)); + Assert.Equal(255, color5.R); + Assert.Equal(Math.Round(.1f * 255), color5.G); + Assert.Equal(Math.Round(.133f * 255), color5.B); + Assert.Equal(Math.Round(.5f * 255), color5.A); + } + [Fact] public void Argb32_PackedValue() { diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs index f145e6928e..0266704850 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs @@ -9,6 +9,24 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats { public class Bgr24Tests { + [Fact] + public void AreEqual() + { + var color1 = new Bgr24(byte.MaxValue, 0, byte.MaxValue); + var color2 = new Bgr24(byte.MaxValue, 0, byte.MaxValue); + + Assert.Equal(color1, color2); + } + + [Fact] + public void AreNotEqual() + { + var color1 = new Bgr24(byte.MaxValue, 0, 0); + var color2 = new Bgr24(byte.MaxValue, 0, byte.MaxValue); + + Assert.NotEqual(color1, color2); + } + public static readonly TheoryData ColorData = new TheoryData() { { 1, 2, 3 }, { 4, 5, 6 }, { 0, 255, 42 } }; diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs index 03e9054667..ccefa85c1e 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; using System.Numerics; using SixLabors.ImageSharp.PixelFormats; using Xunit; diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs index 171a3dfa00..4c5de7ee91 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; using System.Numerics; using SixLabors.ImageSharp.PixelFormats; using Xunit; @@ -9,6 +10,30 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats { public class Bgra32Tests { + /// + /// Tests the equality operators for equality. + /// + [Fact] + public void AreEqual() + { + var color1 = new Bgra32(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); + var color2 = new Bgra32(byte.MaxValue, byte.MaxValue, byte.MaxValue); + + Assert.Equal(color1, color2); + } + + /// + /// Tests the equality operators for inequality. + /// + [Fact] + public void AreNotEqual() + { + var color1 = new Bgra32(0, 0, byte.MaxValue, byte.MaxValue); + var color2 = new Bgra32(byte.MaxValue, byte.MaxValue, byte.MaxValue); + + Assert.NotEqual(color1, color2); + } + public static readonly TheoryData ColorData = new TheoryData() { diff --git a/tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs index ce9e56317e..2dff656ac8 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs @@ -75,6 +75,20 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(1, actual.W); } + [Fact] + public void Byte4_ToRgba32() + { + // arrange + var byte4 = new Byte4(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); + var expected = new Rgba32(Vector4.One); + var actual = default(Rgba32); + + // act + byte4.ToRgba32(ref actual); + + Assert.Equal(expected, actual); + } + [Fact] public void Byte4_FromScaledVector4() { diff --git a/tests/ImageSharp.Tests/PixelFormats/Gray16Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Gray16Tests.cs index 01b60832e2..8a0bd62c19 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Gray16Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Gray16Tests.cs @@ -9,6 +9,24 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats { public class Gray16Tests { + [Fact] + public void AreEqual() + { + var color1 = new Gray16(3000); + var color2 = new Gray16(3000); + + Assert.Equal(color1, color2); + } + + [Fact] + public void AreNotEqual() + { + var color1 = new Gray16(12345); + var color2 = new Gray16(54321); + + Assert.NotEqual(color1, color2); + } + [Theory] [InlineData(0)] [InlineData(65535)] diff --git a/tests/ImageSharp.Tests/PixelFormats/Gray8Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Gray8Tests.cs index 159f97cdee..74fd903ca9 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Gray8Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Gray8Tests.cs @@ -1,10 +1,8 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System.Diagnostics; using System.Numerics; -using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.PixelFormats; using Xunit; @@ -45,6 +43,24 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats public void Gray8_PackedValue_EqualsInput(byte input) => Assert.Equal(input, new Gray8(input).PackedValue); + [Fact] + public void AreEqual() + { + var color1 = new Gray8(100); + var color2 = new Gray8(100); + + Assert.Equal(color1, color2); + } + + [Fact] + public void AreNotEqual() + { + var color1 = new Gray8(100); + var color2 = new Gray8(200); + + Assert.NotEqual(color1, color2); + } + [Fact] public void Gray8_FromScaledVector4() { diff --git a/tests/ImageSharp.Tests/PixelFormats/NormalizedByte4Tests.cs b/tests/ImageSharp.Tests/PixelFormats/NormalizedByte4Tests.cs index 1cb404a008..7687a7777c 100644 --- a/tests/ImageSharp.Tests/PixelFormats/NormalizedByte4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/NormalizedByte4Tests.cs @@ -9,6 +9,36 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats { public class NormalizedByte4Tests { + /// + /// Tests the equality operators for equality. + /// + [Fact] + public void AreEqual() + { + var color1 = new NormalizedByte4(0.0f, 0.0f, 0.0f, 0.0f); + var color2 = new NormalizedByte4(new Vector4(0.0f)); + var color3 = new NormalizedByte4(new Vector4(1.0f, 0.0f, 1.0f, 1.0f)); + var color4 = new NormalizedByte4(1.0f, 0.0f, 1.0f, 1.0f); + + Assert.Equal(color1, color2); + Assert.Equal(color3, color4); + } + + /// + /// Tests the equality operators for inequality. + /// + [Fact] + public void AreNotEqual() + { + var color1 = new NormalizedByte4(0.0f, 0.0f, 0.0f, 0.0f); + var color2 = new NormalizedByte4(new Vector4(1.0f)); + var color3 = new NormalizedByte4(new Vector4(1.0f, 0.0f, 0.0f, 1.0f)); + var color4 = new NormalizedByte4(1.0f, 1.0f, 0.0f, 1.0f); + + Assert.NotEqual(color1, color2); + Assert.NotEqual(color3, color4); + } + [Fact] public void NormalizedByte4_PackedValues() { @@ -61,6 +91,90 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(expected, actual); } + [Fact] + public void NormalizedByte4_FromArgb32() + { + // arrange + var byte4 = default(NormalizedByte4); + Vector4 expected = Vector4.One; + + // act + byte4.FromArgb32(new Argb32(255, 255, 255, 255)); + + // assert + Assert.Equal(expected, byte4.ToScaledVector4()); + } + + [Fact] + public void NormalizedByte4_FromBgr24() + { + // arrange + var byte4 = default(NormalizedByte4); + Vector4 expected = Vector4.One; + + // act + byte4.FromBgr24(new Bgr24(byte.MaxValue, byte.MaxValue, byte.MaxValue)); + + // assert + Assert.Equal(expected, byte4.ToScaledVector4()); + } + + [Fact] + public void NormalizedByte4_FromGrey8() + { + // arrange + var byte4 = default(NormalizedByte4); + Vector4 expected = Vector4.One; + + // act + byte4.FromGray8(new Gray8(byte.MaxValue)); + + // assert + Assert.Equal(expected, byte4.ToScaledVector4()); + } + + [Fact] + public void NormalizedByte4_FromGrey16() + { + // arrange + var byte4 = default(NormalizedByte4); + Vector4 expected = Vector4.One; + + // act + byte4.FromGray16(new Gray16(ushort.MaxValue)); + + // assert + Assert.Equal(expected, byte4.ToScaledVector4()); + } + + [Fact] + public void NormalizedByte4_FromRgb24() + { + // arrange + var byte4 = default(NormalizedByte4); + Vector4 expected = Vector4.One; + + // act + byte4.FromRgb24(new Rgb24(byte.MaxValue, byte.MaxValue, byte.MaxValue)); + + // assert + Assert.Equal(expected, byte4.ToScaledVector4()); + } + + [Fact] + public void NormalizedByte4_FromRgba32() + { + // arrange + var byte4 = default(NormalizedByte4); + Vector4 expected = Vector4.One; + + // act + byte4.FromRgba32(new Rgba32(255, 255, 255, 255)); + + // assert + Assert.Equal(expected, byte4.ToScaledVector4()); + } + [Fact] public void NormalizedByte4_FromBgra5551() { @@ -74,5 +188,47 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats // assert Assert.Equal(expected, normalizedByte4.ToVector4()); } + + [Fact] + public void NormalizedByte4_FromRgb48() + { + // arrange + var byte4 = default(NormalizedByte4); + Vector4 expected = Vector4.One; + + // act + byte4.FromRgb48(new Rgb48(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)); + + // assert + Assert.Equal(expected, byte4.ToScaledVector4()); + } + + [Fact] + public void NormalizedByte4_FromRgba64() + { + // arrange + var byte4 = default(NormalizedByte4); + Vector4 expected = Vector4.One; + + // act + byte4.FromRgba64(new Rgba64(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)); + + // assert + Assert.Equal(expected, byte4.ToScaledVector4()); + } + + [Fact] + public void NormalizedByte4_ToRgba32() + { + // arrange + var byte4 = new NormalizedByte4(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); + var expected = new Rgba32(Vector4.One); + var actual = default(Rgba32); + + // act + byte4.ToRgba32(ref actual); + + Assert.Equal(expected, actual); + } } } diff --git a/tests/ImageSharp.Tests/PixelFormats/NormalizedShort4Tests.cs b/tests/ImageSharp.Tests/PixelFormats/NormalizedShort4Tests.cs index cea7e3146e..b872e58b68 100644 --- a/tests/ImageSharp.Tests/PixelFormats/NormalizedShort4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/NormalizedShort4Tests.cs @@ -9,6 +9,36 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats { public class NormalizedShort4Tests { + /// + /// Tests the equality operators for equality. + /// + [Fact] + public void AreEqual() + { + var color1 = new NormalizedShort4(0.0f, 0.0f, 0.0f, 0.0f); + var color2 = new NormalizedShort4(new Vector4(0.0f)); + var color3 = new NormalizedShort4(new Vector4(1.0f, 0.0f, 1.0f, 1.0f)); + var color4 = new NormalizedShort4(1.0f, 0.0f, 1.0f, 1.0f); + + Assert.Equal(color1, color2); + Assert.Equal(color3, color4); + } + + /// + /// Tests the equality operators for inequality. + /// + [Fact] + public void AreNotEqual() + { + var color1 = new NormalizedShort4(0.0f, 0.0f, 0.0f, 0.0f); + var color2 = new NormalizedShort4(new Vector4(1.0f)); + var color3 = new NormalizedShort4(new Vector4(1.0f, 0.0f, 0.0f, 1.0f)); + var color4 = new NormalizedShort4(1.0f, 1.0f, 0.0f, 1.0f); + + Assert.NotEqual(color1, color2); + Assert.NotEqual(color3, color4); + } + [Fact] public void NormalizedShort4_PackedValues() { @@ -62,6 +92,132 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(expected, actual); } + [Fact] + public void NormalizedShort4_FromArgb32() + { + // arrange + var byte4 = default(NormalizedShort4); + Vector4 expected = Vector4.One; + + // act + byte4.FromArgb32(new Argb32(255, 255, 255, 255)); + + // assert + Assert.Equal(expected, byte4.ToScaledVector4()); + } + + [Fact] + public void NormalizedShort4_FromBgr24() + { + // arrange + var byte4 = default(NormalizedShort4); + Vector4 expected = Vector4.One; + + // act + byte4.FromBgr24(new Bgr24(byte.MaxValue, byte.MaxValue, byte.MaxValue)); + + // assert + Assert.Equal(expected, byte4.ToScaledVector4()); + } + + [Fact] + public void NormalizedShort4_FromGrey8() + { + // arrange + var byte4 = default(NormalizedShort4); + Vector4 expected = Vector4.One; + + // act + byte4.FromGray8(new Gray8(byte.MaxValue)); + + // assert + Assert.Equal(expected, byte4.ToScaledVector4()); + } + + [Fact] + public void NormalizedShort4_FromGrey16() + { + // arrange + var byte4 = default(NormalizedShort4); + Vector4 expected = Vector4.One; + + // act + byte4.FromGray16(new Gray16(ushort.MaxValue)); + + // assert + Assert.Equal(expected, byte4.ToScaledVector4()); + } + + [Fact] + public void NormalizedShort4_FromRgb24() + { + // arrange + var byte4 = default(NormalizedShort4); + Vector4 expected = Vector4.One; + + // act + byte4.FromRgb24(new Rgb24(byte.MaxValue, byte.MaxValue, byte.MaxValue)); + + // assert + Assert.Equal(expected, byte4.ToScaledVector4()); + } + + [Fact] + public void NormalizedShort4_FromRgba32() + { + // arrange + var byte4 = default(NormalizedShort4); + Vector4 expected = Vector4.One; + + // act + byte4.FromRgba32(new Rgba32(255, 255, 255, 255)); + + // assert + Assert.Equal(expected, byte4.ToScaledVector4()); + } + + [Fact] + public void NormalizedShort4_FromRgb48() + { + // arrange + var byte4 = default(NormalizedShort4); + Vector4 expected = Vector4.One; + + // act + byte4.FromRgb48(new Rgb48(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)); + + // assert + Assert.Equal(expected, byte4.ToScaledVector4()); + } + + [Fact] + public void NormalizedShort4_FromRgba64() + { + // arrange + var byte4 = default(NormalizedShort4); + Vector4 expected = Vector4.One; + + // act + byte4.FromRgba64(new Rgba64(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)); + + // assert + Assert.Equal(expected, byte4.ToScaledVector4()); + } + + [Fact] + public void NormalizedShort4_ToRgba32() + { + // arrange + var byte4 = new NormalizedShort4(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); + var expected = new Rgba32(Vector4.One); + var actual = default(Rgba32); + + // act + byte4.ToRgba32(ref actual); + + Assert.Equal(expected, actual); + } + [Fact] public void NormalizedShort4_FromBgra5551() { diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs index 19a5d4b22e..3a28c82b7b 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs @@ -122,6 +122,24 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(expectedPackedValue, rgba.PackedValue); } + [Fact] + public void Rgba1010102_FromRgba32() + { + // arrange + var rgba1 = default(Rgba1010102); + var rgba2 = default(Rgba1010102); + uint expectedPackedValue1 = uint.MaxValue; + uint expectedPackedValue2 = 0xFFF003FF; + + // act + rgba1.FromRgba32(new Rgba32(255, 255, 255, 255)); + rgba2.FromRgba32(new Rgba32(255, 0, 255, 255)); + + // assert + Assert.Equal(expectedPackedValue1, rgba1.PackedValue); + Assert.Equal(expectedPackedValue2, rgba2.PackedValue); + } + [Fact] public void Rgba1010102_FromBgr24() { diff --git a/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs b/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs index e880e38517..570d72cf49 100644 --- a/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs @@ -146,5 +146,47 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats // assert Assert.Equal(expected, actual); } + + [Fact] + public void RgbaVector_FromBgra5551() + { + // arrange + var rgb = default(RgbaVector); + Vector4 expected = Vector4.One; + + // act + rgb.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + + // assert + Assert.Equal(expected, rgb.ToScaledVector4()); + } + + [Fact] + public void RgbaVector_FromGrey16() + { + // arrange + var rgba = default(RgbaVector); + Vector4 expected = Vector4.One; + + // act + rgba.FromGray16(new Gray16(ushort.MaxValue)); + + // assert + Assert.Equal(expected, rgba.ToScaledVector4()); + } + + [Fact] + public void RgbaVector_FromGrey8() + { + // arrange + var rgba = default(RgbaVector); + Vector4 expected = Vector4.One; + + // act + rgba.FromGray8(new Gray8(byte.MaxValue)); + + // assert + Assert.Equal(expected, rgba.ToScaledVector4()); + } } } \ No newline at end of file From 5afc44aa23afaabcd4c328d24223923b506bfe55 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 4 May 2019 22:18:22 +0200 Subject: [PATCH 123/223] skip DitherTests on old 32 bit runtimes --- .../Processors/Dithering/DitherTests.cs | 37 +++++++++++++++++++ tests/Images/External | 2 +- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs index 368feefb91..cb901c2a88 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs @@ -46,11 +46,23 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization private static IErrorDiffuser DefaultErrorDiffuser => KnownDiffusers.Atkinson; + /// + /// The output is visually correct old 32bit runtime, + /// but it is very different because of floating point inaccuracies. + /// + private static readonly bool SkipAllDitherTests = + !TestEnvironment.Is64BitProcess && string.IsNullOrEmpty(TestEnvironment.NetCoreVersion); + [Theory] [WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Rgba32)] public void ApplyDiffusionFilterInBox(TestImageProvider provider) where TPixel : struct, IPixel { + if (SkipAllDitherTests) + { + return; + } + provider.RunRectangleConstrainedValidatingProcessorTest( (x, rect) => x.Diffuse(DefaultErrorDiffuser, .5F, rect), comparer: ValidatorComparer); @@ -61,6 +73,11 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization public void ApplyDitherFilterInBox(TestImageProvider provider) where TPixel : struct, IPixel { + if (SkipAllDitherTests) + { + return; + } + provider.RunRectangleConstrainedValidatingProcessorTest( (x, rect) => x.Dither(DefaultDitherer, rect), comparer: ValidatorComparer); @@ -71,6 +88,11 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization public void DiffusionFilter_ShouldNotDependOnSinglePixelType(TestImageProvider provider) where TPixel : struct, IPixel { + if (SkipAllDitherTests) + { + return; + } + // Increased tolerance because of compatibility issues on .NET 4.6.2: var comparer = ImageComparer.TolerantPercentage(1f); provider.RunValidatingProcessorTest(x => x.Diffuse(DefaultErrorDiffuser, 0.5f), comparer: comparer); @@ -83,6 +105,11 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization IErrorDiffuser diffuser) where TPixel : struct, IPixel { + if (SkipAllDitherTests) + { + return; + } + provider.RunValidatingProcessorTest( x => x.Diffuse(diffuser, 0.5f), testOutputDetails: diffuser.GetType().Name, @@ -95,6 +122,11 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization public void DitherFilter_ShouldNotDependOnSinglePixelType(TestImageProvider provider) where TPixel : struct, IPixel { + if (SkipAllDitherTests) + { + return; + } + provider.RunValidatingProcessorTest( x => x.Dither(DefaultDitherer), comparer: ValidatorComparer); @@ -107,6 +139,11 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization IOrderedDither ditherer) where TPixel : struct, IPixel { + if (SkipAllDitherTests) + { + return; + } + provider.RunValidatingProcessorTest( x => x.Dither(ditherer), testOutputDetails: ditherer.GetType().Name, diff --git a/tests/Images/External b/tests/Images/External index 854ba68b05..c057090b44 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 854ba68b05162bdd87c0c9bbdcdb1c848aa6ddbe +Subproject commit c057090b4402120a83a8efe251aa5b691db9c0dc From 604a8008fb3ab9d0fe173dc20d61471a69d8474a Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 4 May 2019 23:16:11 +0200 Subject: [PATCH 124/223] refactor BoxBlur and GaussianBlur --- .../Processing/BoxBlurExtensions.cs | 20 ++--- .../Processing/GaussianBlurExtensions.cs | 20 ++--- .../Convolution/BoxBlurProcessor.cs | 53 ++++-------- .../Convolution/BoxBlurProcessor{TPixel}.cs | 64 +++++++++++++++ .../Convolution/GaussianBlurProcessor.cs | 82 +++++++------------ .../GaussianBlurProcessor{TPixel}.cs | 79 ++++++++++++++++++ .../Processing/Convolution/BoxBlurTest.cs | 6 +- .../Convolution/GaussianBlurTest.cs | 6 +- 8 files changed, 210 insertions(+), 120 deletions(-) create mode 100644 src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs create mode 100644 src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs diff --git a/src/ImageSharp/Processing/BoxBlurExtensions.cs b/src/ImageSharp/Processing/BoxBlurExtensions.cs index 624da239bb..bc6e06dd67 100644 --- a/src/ImageSharp/Processing/BoxBlurExtensions.cs +++ b/src/ImageSharp/Processing/BoxBlurExtensions.cs @@ -8,43 +8,37 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds box blurring extensions to the type. + /// Adds box blurring extensions to the type. /// public static class BoxBlurExtensions { /// /// Applies a box blur to the image. /// - /// The pixel format. /// The image this method extends. /// The . - public static IImageProcessingContext BoxBlur(this IImageProcessingContext source) - where TPixel : struct, IPixel - => source.ApplyProcessor(new BoxBlurProcessor()); + public static IImageProcessingContext BoxBlur(this IImageProcessingContext source) + => source.ApplyProcessor(new BoxBlurProcessor()); /// /// Applies a box blur to the image. /// - /// The pixel format. /// The image this method extends. /// The 'radius' value representing the size of the area to sample. /// The . - public static IImageProcessingContext BoxBlur(this IImageProcessingContext source, int radius) - where TPixel : struct, IPixel - => source.ApplyProcessor(new BoxBlurProcessor(radius)); + public static IImageProcessingContext BoxBlur(this IImageProcessingContext source, int radius) + => source.ApplyProcessor(new BoxBlurProcessor(radius)); /// /// Applies a box blur to the image. /// - /// The pixel format. /// The image this method extends. /// The 'radius' value representing the size of the area to sample. /// /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext BoxBlur(this IImageProcessingContext source, int radius, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new BoxBlurProcessor(radius), rectangle); + public static IImageProcessingContext BoxBlur(this IImageProcessingContext source, int radius, Rectangle rectangle) + => source.ApplyProcessor(new BoxBlurProcessor(radius), rectangle); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/GaussianBlurExtensions.cs b/src/ImageSharp/Processing/GaussianBlurExtensions.cs index 165c4ce1a6..1dab054579 100644 --- a/src/ImageSharp/Processing/GaussianBlurExtensions.cs +++ b/src/ImageSharp/Processing/GaussianBlurExtensions.cs @@ -8,43 +8,37 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds Gaussian blurring extensions to the type. + /// Adds Gaussian blurring extensions to the type. /// public static class GaussianBlurExtensions { /// /// Applies a Gaussian blur to the image. /// - /// The pixel format. /// The image this method extends. /// The . - public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source) - where TPixel : struct, IPixel - => source.ApplyProcessor(new GaussianBlurProcessor()); + public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source) + => source.ApplyProcessor(new GaussianBlurProcessor()); /// /// Applies a Gaussian blur to the image. /// - /// The pixel format. /// The image this method extends. /// The 'sigma' value representing the weight of the blur. /// The . - public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source, float sigma) - where TPixel : struct, IPixel - => source.ApplyProcessor(new GaussianBlurProcessor(sigma)); + public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source, float sigma) + => source.ApplyProcessor(new GaussianBlurProcessor(sigma)); /// /// Applies a Gaussian blur to the image. /// - /// The pixel format. /// The image this method extends. /// The 'sigma' value representing the weight of the blur. /// /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source, float sigma, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new GaussianBlurProcessor(sigma), rectangle); + public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source, float sigma, Rectangle rectangle) + => source.ApplyProcessor(new GaussianBlurProcessor(sigma), rectangle); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs index 3d5bdc42a7..bb37489e13 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs @@ -3,67 +3,48 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// - /// Applies box blur processing to the image. + /// Defines a box blur processor of a given Radius. /// - /// The pixel format. - internal class BoxBlurProcessor : ImageProcessor - where TPixel : struct, IPixel + public class BoxBlurProcessor : IImageProcessor { /// - /// The maximum size of the kernel in either direction. + /// The default radius used by the parameterless constructor. /// - private readonly int kernelSize; + public const int DefaultRadius = 7; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The 'radius' value representing the size of the area to sample. /// - public BoxBlurProcessor(int radius = 7) + public BoxBlurProcessor(int radius) { this.Radius = radius; - this.kernelSize = (radius * 2) + 1; - this.KernelX = this.CreateBoxKernel(); - this.KernelY = this.KernelX.Transpose(); } /// - /// Gets the Radius + /// Initializes a new instance of the class. /// - public int Radius { get; } - - /// - /// Gets the horizontal gradient operator. - /// - public DenseMatrix KernelX { get; } + public BoxBlurProcessor() + : this(DefaultRadius) + { + } /// - /// Gets the vertical gradient operator. + /// Gets the Radius. /// - public DenseMatrix KernelY { get; } - - /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - => new Convolution2PassProcessor(this.KernelX, this.KernelY, false).Apply(source, sourceRectangle, configuration); + public int Radius { get; } - /// - /// Create a 1 dimensional Box kernel. - /// - /// The - private DenseMatrix CreateBoxKernel() + /// + public IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel { - int size = this.kernelSize; - var kernel = new DenseMatrix(size, 1); - - kernel.Fill(1F / size); - - return kernel; + return new BoxBlurProcessor(this); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs new file mode 100644 index 0000000000..ba092cb424 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs @@ -0,0 +1,64 @@ +// // Copyright (c) Six Labors and contributors. +// // Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Primitives; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Convolution +{ + /// + /// Applies box blur processing to the image. + /// + /// The pixel format. + internal class BoxBlurProcessor : ImageProcessor + where TPixel : struct, IPixel + { + private readonly BoxBlurProcessor definition; + + /// + /// Initializes a new instance of the class. + /// + /// The defining the processor parameters. + public BoxBlurProcessor(BoxBlurProcessor definition) + { + this.definition = definition; + int kernelSize = (definition.Radius * 2) + 1; + this.KernelX = CreateBoxKernel(kernelSize); + this.KernelY = this.KernelX.Transpose(); + } + + /// + /// Gets the horizontal gradient operator. + /// + public DenseMatrix KernelX { get; } + + /// + /// Gets the vertical gradient operator. + /// + public DenseMatrix KernelY { get; } + + + /// + protected override void OnFrameApply( + ImageFrame source, + Rectangle sourceRectangle, + Configuration configuration) => + new Convolution2PassProcessor(this.KernelX, this.KernelY, false).Apply( + source, + sourceRectangle, + configuration); + + /// + /// Create a 1 dimensional Box kernel. + /// + /// The maximum size of the kernel in either direction. + /// The . + private static DenseMatrix CreateBoxKernel(int kernelSize) + { + var kernel = new DenseMatrix(kernelSize, 1); + kernel.Fill(1F / kernelSize); + return kernel; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs index 0fc822d57c..33e2ef955d 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs @@ -4,35 +4,38 @@ using System; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// - /// Applies Gaussian blur processing to the image. + /// Defines a gaussian blur processor with a (Sigma, Radius) pair. /// - /// The pixel format. - internal class GaussianBlurProcessor : ImageProcessor - where TPixel : struct, IPixel + public class GaussianBlurProcessor : IImageProcessor { /// - /// The maximum size of the kernel in either direction. + /// The default value for . /// - private readonly int kernelSize; + public const float DefaultSigma = 3f; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. + /// + public GaussianBlurProcessor() + : this(DefaultSigma, CalculateDefaultRadius(DefaultSigma)) + { + } + + /// + /// Initializes a new instance of the class. /// /// The 'sigma' value representing the weight of the blur. - public GaussianBlurProcessor(float sigma = 3F) - : this(sigma, (int)MathF.Ceiling(sigma * 3)) + public GaussianBlurProcessor(float sigma) + : this(sigma, CalculateDefaultRadius(sigma)) { - // Kernel radius is calculated using the minimum viable value. - // http://chemaguerra.com/gaussian-filter-radius/ } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The 'radius' value representing the size of the area to sample. @@ -43,7 +46,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The 'sigma' value representing the weight of the blur. @@ -54,10 +57,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// public GaussianBlurProcessor(float sigma, int radius) { - this.kernelSize = (radius * 2) + 1; this.Sigma = sigma; - this.KernelX = this.CreateGaussianKernel(); - this.KernelY = this.KernelX.Transpose(); + this.Radius = radius; } /// @@ -66,47 +67,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution public float Sigma { get; } /// - /// Gets the horizontal gradient operator. - /// - public DenseMatrix KernelX { get; } - - /// - /// Gets the vertical gradient operator. + /// Gets the radius defining the size of the area to sample. /// - public DenseMatrix KernelY { get; } + public int Radius { get; } - /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - => new Convolution2PassProcessor(this.KernelX, this.KernelY, false).Apply(source, sourceRectangle, configuration); + /// + public IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel + { + return new GaussianBlurProcessor(this); + } /// - /// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function + /// Kernel radius is calculated using the minimum viable value. + /// . /// - /// The - private DenseMatrix CreateGaussianKernel() + private static int CalculateDefaultRadius(float sigma) { - int size = this.kernelSize; - float weight = this.Sigma; - var kernel = new DenseMatrix(size, 1); - - float sum = 0F; - float midpoint = (size - 1) / 2F; - - for (int i = 0; i < size; i++) - { - float x = i - midpoint; - float gx = ImageMaths.Gaussian(x, weight); - sum += gx; - kernel[0, i] = gx; - } - - // Normalize kernel so that the sum of all weights equals 1 - for (int i = 0; i < size; i++) - { - kernel[0, i] /= sum; - } - - return kernel; + return (int)MathF.Ceiling(sigma * 3); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs new file mode 100644 index 0000000000..9b60fb2d2e --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs @@ -0,0 +1,79 @@ +// // Copyright (c) Six Labors and contributors. +// // Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Primitives; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Convolution +{ + /// + /// Applies Gaussian blur processing to an image. + /// + /// The pixel format. + internal class GaussianBlurProcessor : ImageProcessor + where TPixel : struct, IPixel + { + private readonly GaussianBlurProcessor definition; + + /// + /// Initializes a new instance of the class. + /// + /// The defining the processor parameters. + public GaussianBlurProcessor(GaussianBlurProcessor definition) + { + this.definition = definition; + int kernelSize = (definition.Radius * 2) + 1; + this.KernelX = CreateGaussianKernel(kernelSize, definition.Sigma); + this.KernelY = this.KernelX.Transpose(); + } + + /// + /// Gets the horizontal gradient operator. + /// + public DenseMatrix KernelX { get; } + + /// + /// Gets the vertical gradient operator. + /// + public DenseMatrix KernelY { get; } + + /// + protected override void OnFrameApply( + ImageFrame source, + Rectangle sourceRectangle, + Configuration configuration) => + new Convolution2PassProcessor(this.KernelX, this.KernelY, false).Apply( + source, + sourceRectangle, + configuration); + + /// + /// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function + /// + /// The + private static DenseMatrix CreateGaussianKernel(int size, float weight) + { + var kernel = new DenseMatrix(size, 1); + + float sum = 0F; + float midpoint = (size - 1) / 2F; + + for (int i = 0; i < size; i++) + { + float x = i - midpoint; + float gx = ImageMaths.Gaussian(x, weight); + sum += gx; + kernel[0, i] = gx; + } + + // Normalize kernel so that the sum of all weights equals 1 + for (int i = 0; i < size; i++) + { + kernel[0, i] /= sum; + } + + return kernel; + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Convolution/BoxBlurTest.cs b/tests/ImageSharp.Tests/Processing/Convolution/BoxBlurTest.cs index e425b63151..8c659848fc 100644 --- a/tests/ImageSharp.Tests/Processing/Convolution/BoxBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Convolution/BoxBlurTest.cs @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Convolution public void BoxBlur_BoxBlurProcessorDefaultsSet() { this.operations.BoxBlur(); - var processor = this.Verify>(); + var processor = this.Verify(); Assert.Equal(7, processor.Radius); } @@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Convolution public void BoxBlur_amount_BoxBlurProcessorDefaultsSet() { this.operations.BoxBlur(34); - var processor = this.Verify>(); + var processor = this.Verify(); Assert.Equal(34, processor.Radius); } @@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Convolution public void BoxBlur_amount_rect_BoxBlurProcessorDefaultsSet() { this.operations.BoxBlur(5, this.rect); - var processor = this.Verify>(this.rect); + var processor = this.Verify(this.rect); Assert.Equal(5, processor.Radius); } diff --git a/tests/ImageSharp.Tests/Processing/Convolution/GaussianBlurTest.cs b/tests/ImageSharp.Tests/Processing/Convolution/GaussianBlurTest.cs index c87a834eb6..0f64ebbeb4 100644 --- a/tests/ImageSharp.Tests/Processing/Convolution/GaussianBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Convolution/GaussianBlurTest.cs @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Convolution public void GaussianBlur_GaussianBlurProcessorDefaultsSet() { this.operations.GaussianBlur(); - var processor = this.Verify>(); + var processor = this.Verify(); Assert.Equal(3f, processor.Sigma); } @@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Convolution public void GaussianBlur_amount_GaussianBlurProcessorDefaultsSet() { this.operations.GaussianBlur(0.2f); - var processor = this.Verify>(); + var processor = this.Verify(); Assert.Equal(.2f, processor.Sigma); } @@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Convolution public void GaussianBlur_amount_rect_GaussianBlurProcessorDefaultsSet() { this.operations.GaussianBlur(0.6f, this.rect); - var processor = this.Verify>(this.rect); + var processor = this.Verify(this.rect); Assert.Equal(.6f, processor.Sigma); } From 5fc4374fd9d0645fb7b808b2858321c3c6ce51d8 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 4 May 2019 23:27:28 +0200 Subject: [PATCH 125/223] making filters public --- .../Filters/AchromatomalyProcessor.cs | 2 +- .../Filters/AchromatopsiaProcessor.cs | 2 +- .../Processors/Filters/BlackWhiteProcessor.cs | 4 +- .../Processors/Filters/BrightnessProcessor.cs | 2 +- .../Processors/Filters/ContrastProcessor.cs | 4 +- .../Filters/DeuteranomalyProcessor.cs | 2 +- .../Filters/DeuteranopiaProcessor.cs | 2 +- .../Processors/Filters/FilterProcessor.cs | 4 +- .../Filters/GrayscaleBt601Processor.cs | 2 +- .../Filters/GrayscaleBt709Processor.cs | 2 +- .../Processors/Filters/HueProcessor.cs | 2 +- .../Processors/Filters/InvertProcessor.cs | 2 +- .../Processors/Filters/KodachromeProcessor.cs | 2 +- .../Processors/Filters/LomographProcessor.cs | 3 +- .../Filters/LomographProcessor{TPixel}.cs | 4 ++ .../Processors/Filters/OpacityProcessor.cs | 4 +- .../Filters/PolaroidProcessor{TPixel}.cs | 6 +- .../Filters/ProtanomalyProcessor.cs | 2 +- .../Processors/Filters/ProtanopiaProcessor.cs | 2 +- .../Processors/Filters/SaturateProcessor.cs | 2 +- .../Processors/Filters/SepiaProcessor.cs | 2 +- .../Filters/TritanomalyProcessor.cs | 2 +- .../Processors/Filters/TritanopiaProcessor.cs | 2 +- .../Processing/Processors/IImageProcessor.cs | 56 +------------------ .../Processors/IImageProcessor{TPixel}.cs | 31 ++++++++++ .../Processors/ImageProcessorExtensions.cs | 37 ++++++++++++ 26 files changed, 106 insertions(+), 79 deletions(-) create mode 100644 src/ImageSharp/Processing/Processors/IImageProcessor{TPixel}.cs create mode 100644 src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs diff --git a/src/ImageSharp/Processing/Processors/Filters/AchromatomalyProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/AchromatomalyProcessor.cs index 2dac6b827d..7c3789c388 100644 --- a/src/ImageSharp/Processing/Processors/Filters/AchromatomalyProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/AchromatomalyProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating Achromatomaly (Color desensitivity) color blindness. /// - internal class AchromatomalyProcessor : FilterProcessor + public class AchromatomalyProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/AchromatopsiaProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/AchromatopsiaProcessor.cs index 4df94a7603..ab04a30ba1 100644 --- a/src/ImageSharp/Processing/Processors/Filters/AchromatopsiaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/AchromatopsiaProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating Achromatopsia (Monochrome) color blindness. /// - internal class AchromatopsiaProcessor : FilterProcessor + public class AchromatopsiaProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/BlackWhiteProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/BlackWhiteProcessor.cs index 698f11cbf0..7e2e7f2f47 100644 --- a/src/ImageSharp/Processing/Processors/Filters/BlackWhiteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/BlackWhiteProcessor.cs @@ -6,9 +6,9 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// - /// Applies a black and white filter matrix to the image + /// Applies a black and white filter matrix to the image. /// - internal class BlackWhiteProcessor : FilterProcessor + public class BlackWhiteProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/BrightnessProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/BrightnessProcessor.cs index 6a1f3869ae..94a7a394a6 100644 --- a/src/ImageSharp/Processing/Processors/Filters/BrightnessProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/BrightnessProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies a brightness filter matrix using the given amount. /// - internal class BrightnessProcessor : FilterProcessor + public class BrightnessProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/ContrastProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/ContrastProcessor.cs index 3bea695b99..1f74a1d642 100644 --- a/src/ImageSharp/Processing/Processors/Filters/ContrastProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/ContrastProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies a contrast filter matrix using the given amount. /// - internal class ContrastProcessor : FilterProcessor + public class ContrastProcessor : FilterProcessor { /// /// Initializes a new instance of the class. @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters } /// - /// Gets the proportion of the conversion + /// Gets the proportion of the conversion. /// public float Amount { get; } } diff --git a/src/ImageSharp/Processing/Processors/Filters/DeuteranomalyProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/DeuteranomalyProcessor.cs index 395489d563..7ad0630aac 100644 --- a/src/ImageSharp/Processing/Processors/Filters/DeuteranomalyProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/DeuteranomalyProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating Deuteranomaly (Green-Weak) color blindness. /// - internal class DeuteranomalyProcessor : FilterProcessor + public class DeuteranomalyProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/DeuteranopiaProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/DeuteranopiaProcessor.cs index 51f8361e6c..188d02dc8b 100644 --- a/src/ImageSharp/Processing/Processors/Filters/DeuteranopiaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/DeuteranopiaProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating Deuteranopia (Green-Blind) color blindness. /// - internal class DeuteranopiaProcessor : FilterProcessor + public class DeuteranopiaProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs index b615b7bc60..36c43d8446 100644 --- a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; @@ -7,7 +7,7 @@ using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// - /// Provides methods that accept a matrix to apply free-form filters to images. + /// Defines a free-form color filter by a . /// public class FilterProcessor : IImageProcessor { diff --git a/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt601Processor.cs b/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt601Processor.cs index b39e01b9a8..c24992b441 100644 --- a/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt601Processor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt601Processor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies a grayscale filter matrix using the given amount and the formula as specified by ITU-R Recommendation BT.601 /// - internal class GrayscaleBt601Processor : FilterProcessor + public class GrayscaleBt601Processor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs b/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs index 7bbd915564..dc5294ccf0 100644 --- a/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies a grayscale filter matrix using the given amount and the formula as specified by ITU-R Recommendation BT.709 /// - internal class GrayscaleBt709Processor : FilterProcessor + public class GrayscaleBt709Processor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/HueProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/HueProcessor.cs index a69fd50757..65f7eef330 100644 --- a/src/ImageSharp/Processing/Processors/Filters/HueProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/HueProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies a hue filter matrix using the given angle of rotation in degrees /// - internal class HueProcessor : FilterProcessor + public class HueProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/InvertProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/InvertProcessor.cs index d7631970db..cc41e65721 100644 --- a/src/ImageSharp/Processing/Processors/Filters/InvertProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/InvertProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies a filter matrix that inverts the colors of an image /// - internal class InvertProcessor : FilterProcessor + public class InvertProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/KodachromeProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/KodachromeProcessor.cs index 5570f23d7d..a5215a434c 100644 --- a/src/ImageSharp/Processing/Processors/Filters/KodachromeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/KodachromeProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies a filter matrix recreating an old Kodachrome camera effect matrix to the image /// - internal class KodachromeProcessor : FilterProcessor + public class KodachromeProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs index b102f4daf7..dae168e2cb 100644 --- a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating an old Lomograph effect. /// - internal class LomographProcessor : FilterProcessor + public class LomographProcessor : FilterProcessor { /// /// Initializes a new instance of the class. @@ -16,6 +16,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters { } + /// public override IImageProcessor CreatePixelSpecificProcessor() => new LomographProcessor(this); } diff --git a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs index 8ec4ec258a..c15cf6fc90 100644 --- a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs @@ -15,6 +15,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters { private static readonly TPixel VeryDarkGreen = ColorBuilder.FromRGBA(0, 10, 0, 255); + /// + /// Initializes a new instance of the class. + /// + /// The defining the parameters. public LomographProcessor(LomographProcessor definition) : base(definition) { diff --git a/src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs index e790753f3b..0ee5dd59e5 100644 --- a/src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies an opacity filter matrix using the given amount. /// - internal class OpacityProcessor : FilterProcessor + public class OpacityProcessor : FilterProcessor { /// /// Initializes a new instance of the class. @@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters } /// - /// Gets the proportion of the conversion + /// Gets the proportion of the conversion. /// public float Amount { get; } } diff --git a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs index a73aa28de3..befcfc1f42 100644 --- a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs @@ -16,7 +16,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters private static readonly TPixel VeryDarkOrange = ColorBuilder.FromRGB(102, 34, 0); private static readonly TPixel LightOrange = ColorBuilder.FromRGBA(255, 153, 102, 128); - public PolaroidProcessor(FilterProcessor definition) + /// + /// Initializes a new instance of the class. + /// + /// The defining the parameters. + public PolaroidProcessor(PolaroidProcessor definition) : base(definition) { } diff --git a/src/ImageSharp/Processing/Processors/Filters/ProtanomalyProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/ProtanomalyProcessor.cs index 95e369b371..755bc0f81a 100644 --- a/src/ImageSharp/Processing/Processors/Filters/ProtanomalyProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/ProtanomalyProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating Protanomaly (Red-Weak) color blindness. /// - internal class ProtanomalyProcessor : FilterProcessor + public class ProtanomalyProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/ProtanopiaProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/ProtanopiaProcessor.cs index bd92579130..c4f4a76611 100644 --- a/src/ImageSharp/Processing/Processors/Filters/ProtanopiaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/ProtanopiaProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating Protanopia (Red-Blind) color blindness. /// - internal class ProtanopiaProcessor : FilterProcessor + public class ProtanopiaProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/SaturateProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/SaturateProcessor.cs index 424eeb461c..770fb98277 100644 --- a/src/ImageSharp/Processing/Processors/Filters/SaturateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/SaturateProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies a saturation filter matrix using the given amount. /// - internal class SaturateProcessor : FilterProcessor + public class SaturateProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/SepiaProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/SepiaProcessor.cs index 538db4b52c..9da48993a2 100644 --- a/src/ImageSharp/Processing/Processors/Filters/SepiaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/SepiaProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies a sepia filter matrix using the given amount. /// - internal class SepiaProcessor : FilterProcessor + public class SepiaProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/TritanomalyProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/TritanomalyProcessor.cs index 3eda05a115..c64475b994 100644 --- a/src/ImageSharp/Processing/Processors/Filters/TritanomalyProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/TritanomalyProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating Tritanomaly (Blue-Weak) color blindness. /// - internal class TritanomalyProcessor : FilterProcessor + public class TritanomalyProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/TritanopiaProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/TritanopiaProcessor.cs index f5ed26c833..722840e743 100644 --- a/src/ImageSharp/Processing/Processors/Filters/TritanopiaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/TritanopiaProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating Tritanopia (Blue-Blind) color blindness. /// - internal class TritanopiaProcessor : FilterProcessor + public class TritanopiaProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/IImageProcessor.cs b/src/ImageSharp/Processing/Processors/IImageProcessor.cs index e9b2c9c0e3..9b030a6fea 100644 --- a/src/ImageSharp/Processing/Processors/IImageProcessor.cs +++ b/src/ImageSharp/Processing/Processors/IImageProcessor.cs @@ -2,12 +2,14 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors { /// /// Defines an algorithm to alter the pixels of an image. + /// Non-generic implementations are responsible for: + /// 1. Encapsulating the parameters of the algorithm. + /// 2. Creating the generic instance to execute the algorithm. /// public interface IImageProcessor { @@ -20,56 +22,4 @@ namespace SixLabors.ImageSharp.Processing.Processors IImageProcessor CreatePixelSpecificProcessor() where TPixel : struct, IPixel; } - - /// - /// Implements an algorithm to alter the pixels of an image. - /// - /// The pixel format. - public interface IImageProcessor - where TPixel : struct, IPixel - { - /// - /// Applies the process to the specified portion of the specified . - /// - /// The source image. Cannot be null. - /// - /// The structure that specifies the portion of the image object to draw. - /// - /// - /// is null. - /// - /// - /// doesn't fit the dimension of the image. - /// - void Apply(Image source, Rectangle sourceRectangle); - } - - internal static class ImageProcessorExtensions - { - public static void Apply(this IImageProcessor processor, Image source, Rectangle sourceRectangle) - { - var visitor = new ApplyVisitor(processor, sourceRectangle); - source.AcceptVisitor(visitor); - } - - private class ApplyVisitor : IImageVisitor - { - private readonly IImageProcessor processor; - - private readonly Rectangle sourceRectangle; - - public ApplyVisitor(IImageProcessor processor, Rectangle sourceRectangle) - { - this.processor = processor; - this.sourceRectangle = sourceRectangle; - } - - public void Visit(Image image) - where TPixel : struct, IPixel - { - var processorImpl = this.processor.CreatePixelSpecificProcessor(); - processorImpl.Apply(image, this.sourceRectangle); - } - } - } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/IImageProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/IImageProcessor{TPixel}.cs new file mode 100644 index 0000000000..f2c7ef29c1 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/IImageProcessor{TPixel}.cs @@ -0,0 +1,31 @@ +// // Copyright (c) Six Labors and contributors. +// // Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// Implements an algorithm to alter the pixels of an image. + /// + /// The pixel format. + public interface IImageProcessor + where TPixel : struct, IPixel + { + /// + /// Applies the process to the specified portion of the specified . + /// + /// The source image. Cannot be null. + /// + /// The structure that specifies the portion of the image object to draw. + /// + /// + /// is null. + /// + /// + /// doesn't fit the dimension of the image. + /// + void Apply(Image source, Rectangle sourceRectangle); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs b/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs new file mode 100644 index 0000000000..1e189b9c78 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs @@ -0,0 +1,37 @@ +// // Copyright (c) Six Labors and contributors. +// // Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + internal static class ImageProcessorExtensions + { + public static void Apply(this IImageProcessor processor, Image source, Rectangle sourceRectangle) + { + var visitor = new ApplyVisitor(processor, sourceRectangle); + source.AcceptVisitor(visitor); + } + + private class ApplyVisitor : IImageVisitor + { + private readonly IImageProcessor processor; + + private readonly Rectangle sourceRectangle; + + public ApplyVisitor(IImageProcessor processor, Rectangle sourceRectangle) + { + this.processor = processor; + this.sourceRectangle = sourceRectangle; + } + + public void Visit(Image image) + where TPixel : struct, IPixel + { + var processorImpl = this.processor.CreatePixelSpecificProcessor(); + processorImpl.Apply(image, this.sourceRectangle); + } + } + } +} \ No newline at end of file From 432d35ccc322010a4d7f92131353fe1f673c37d8 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 4 May 2019 23:53:00 +0200 Subject: [PATCH 126/223] Further refactor on Gaussian stuff --- .../Processing/GaussianSharpenExtensions.cs | 26 ++--- .../ConvolutionProcessorHelpers.cs | 93 ++++++++++++++++ .../Convolution/GaussianBlurProcessor.cs | 16 +-- .../GaussianBlurProcessor{TPixel}.cs | 37 +------ .../Convolution/GaussianSharpenProcessor.cs | 101 +++++------------- .../GaussianSharpenProcessor{TPixel}.cs | 42 ++++++++ .../Convolution/GaussianSharpenTest.cs | 6 +- 7 files changed, 180 insertions(+), 141 deletions(-) create mode 100644 src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs create mode 100644 src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs diff --git a/src/ImageSharp/Processing/GaussianSharpenExtensions.cs b/src/ImageSharp/Processing/GaussianSharpenExtensions.cs index 675bbc142d..64aa7b5db3 100644 --- a/src/ImageSharp/Processing/GaussianSharpenExtensions.cs +++ b/src/ImageSharp/Processing/GaussianSharpenExtensions.cs @@ -1,50 +1,46 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Convolution; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds Gaussian sharpening extensions to the type. + /// Adds Gaussian sharpening extensions to the type. /// public static class GaussianSharpenExtensions { /// /// Applies a Gaussian sharpening filter to the image. /// - /// The pixel format. /// The image this method extends. /// The . - public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source) - where TPixel : struct, IPixel - => source.ApplyProcessor(new GaussianSharpenProcessor()); + public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source) => + source.ApplyProcessor(new GaussianSharpenProcessor()); /// /// Applies a Gaussian sharpening filter to the image. /// - /// The pixel format. /// The image this method extends. /// The 'sigma' value representing the weight of the blur. /// The . - public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source, float sigma) - where TPixel : struct, IPixel - => source.ApplyProcessor(new GaussianSharpenProcessor(sigma)); + public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source, float sigma) => + source.ApplyProcessor(new GaussianSharpenProcessor(sigma)); /// /// Applies a Gaussian sharpening filter to the image. /// - /// The pixel format. /// The image this method extends. /// The 'sigma' value representing the weight of the blur. /// /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source, float sigma, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new GaussianSharpenProcessor(sigma), rectangle); + public static IImageProcessingContext GaussianSharpen( + this IImageProcessingContext source, + float sigma, + Rectangle rectangle) => + source.ApplyProcessor(new GaussianSharpenProcessor(sigma), rectangle); } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs new file mode 100644 index 0000000000..bb0072b82e --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs @@ -0,0 +1,93 @@ +// // Copyright (c) Six Labors and contributors. +// // Licensed under the Apache License, Version 2.0. + +using System; + +using SixLabors.ImageSharp.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Convolution +{ + internal static class ConvolutionProcessorHelpers + { + /// + /// Kernel radius is calculated using the minimum viable value. + /// . + /// + internal static int GetDefaultGaussianRadius(float sigma) + { + return (int)MathF.Ceiling(sigma * 3); + } + + /// + /// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function + /// + /// The . + internal static DenseMatrix CreateGaussianBlurKernel(int size, float weight) + { + var kernel = new DenseMatrix(size, 1); + + float sum = 0F; + float midpoint = (size - 1) / 2F; + + for (int i = 0; i < size; i++) + { + float x = i - midpoint; + float gx = ImageMaths.Gaussian(x, weight); + sum += gx; + kernel[0, i] = gx; + } + + // Normalize kernel so that the sum of all weights equals 1 + for (int i = 0; i < size; i++) + { + kernel[0, i] /= sum; + } + + return kernel; + } + + /// + /// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function + /// + /// The . + internal static DenseMatrix CreateGaussianSharpenKernel(int size, float weight) + { + var kernel = new DenseMatrix(size, 1); + + float sum = 0; + + float midpoint = (size - 1) / 2F; + for (int i = 0; i < size; i++) + { + float x = i - midpoint; + float gx = ImageMaths.Gaussian(x, weight); + sum += gx; + kernel[0, i] = gx; + } + + // Invert the kernel for sharpening. + int midpointRounded = (int)midpoint; + for (int i = 0; i < size; i++) + { + if (i == midpointRounded) + { + // Calculate central value + kernel[0, i] = (2F * sum) - kernel[0, i]; + } + else + { + // invert value + kernel[0, i] = -kernel[0, i]; + } + } + + // Normalize kernel so that the sum of all weights equals 1 + for (int i = 0; i < size; i++) + { + kernel[0, i] /= sum; + } + + return kernel; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs index 33e2ef955d..c64a024f9f 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs @@ -1,14 +1,13 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// - /// Defines a gaussian blur processor with a (Sigma, Radius) pair. + /// Defines Gaussian blur by a (Sigma, Radius) pair. /// public class GaussianBlurProcessor : IImageProcessor { @@ -21,7 +20,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Initializes a new instance of the class. /// public GaussianBlurProcessor() - : this(DefaultSigma, CalculateDefaultRadius(DefaultSigma)) + : this(DefaultSigma, ConvolutionProcessorHelpers.GetDefaultGaussianRadius(DefaultSigma)) { } @@ -30,7 +29,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// The 'sigma' value representing the weight of the blur. public GaussianBlurProcessor(float sigma) - : this(sigma, CalculateDefaultRadius(sigma)) + : this(sigma, ConvolutionProcessorHelpers.GetDefaultGaussianRadius(sigma)) { } @@ -77,14 +76,5 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { return new GaussianBlurProcessor(this); } - - /// - /// Kernel radius is calculated using the minimum viable value. - /// . - /// - private static int CalculateDefaultRadius(float sigma) - { - return (int)MathF.Ceiling(sigma * 3); - } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs index 9b60fb2d2e..a129ff5473 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs @@ -1,5 +1,5 @@ -// // Copyright (c) Six Labors and contributors. -// // Licensed under the Apache License, Version 2.0. +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; @@ -14,17 +14,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution internal class GaussianBlurProcessor : ImageProcessor where TPixel : struct, IPixel { - private readonly GaussianBlurProcessor definition; - /// /// Initializes a new instance of the class. /// /// The defining the processor parameters. public GaussianBlurProcessor(GaussianBlurProcessor definition) { - this.definition = definition; int kernelSize = (definition.Radius * 2) + 1; - this.KernelX = CreateGaussianKernel(kernelSize, definition.Sigma); + this.KernelX = ConvolutionProcessorHelpers.CreateGaussianBlurKernel(kernelSize, definition.Sigma); this.KernelY = this.KernelX.Transpose(); } @@ -47,33 +44,5 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution source, sourceRectangle, configuration); - - /// - /// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function - /// - /// The - private static DenseMatrix CreateGaussianKernel(int size, float weight) - { - var kernel = new DenseMatrix(size, 1); - - float sum = 0F; - float midpoint = (size - 1) / 2F; - - for (int i = 0; i < size; i++) - { - float x = i - midpoint; - float gx = ImageMaths.Gaussian(x, weight); - sum += gx; - kernel[0, i] = gx; - } - - // Normalize kernel so that the sum of all weights equals 1 - for (int i = 0; i < size; i++) - { - kernel[0, i] /= sum; - } - - return kernel; - } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs index 001471720a..bc06ee6182 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs @@ -3,38 +3,38 @@ using System; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// - /// Applies Gaussian sharpening processing to the image. + /// Defines Gaussian sharpening by a (Sigma, Radius) pair. /// - /// The pixel format. - internal class GaussianSharpenProcessor : ImageProcessor - where TPixel : struct, IPixel + public class GaussianSharpenProcessor : IImageProcessor { + /// + /// The default value for . + /// + public const float DefaultSigma = 3f; + /// - /// The maximum size of the kernel in either direction. + /// Initializes a new instance of the class. /// - private readonly int kernelSize; + public GaussianSharpenProcessor() + : this(DefaultSigma, ConvolutionProcessorHelpers.GetDefaultGaussianRadius(DefaultSigma)) + { + } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// - /// The 'sigma' value representing the weight of the sharpening. - /// - public GaussianSharpenProcessor(float sigma = 3F) - : this(sigma, (int)MathF.Ceiling(sigma * 3)) + /// The 'sigma' value representing the weight of the blur. + public GaussianSharpenProcessor(float sigma) + : this(sigma, ConvolutionProcessorHelpers.GetDefaultGaussianRadius(sigma)) { - // Kernel radius is calculated using the minimum viable value. - // http://chemaguerra.com/gaussian-filter-radius/ } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The 'radius' value representing the size of the area to sample. @@ -45,10 +45,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// - /// The 'sigma' value representing the weight of the sharpen. + /// The 'sigma' value representing the weight of the blur. /// /// /// The 'radius' value representing the size of the area to sample. @@ -56,10 +56,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// public GaussianSharpenProcessor(float sigma, int radius) { - this.kernelSize = (radius * 2) + 1; this.Sigma = sigma; - this.KernelX = this.CreateGaussianKernel(); - this.KernelY = this.KernelX.Transpose(); + this.Radius = radius; } /// @@ -68,63 +66,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution public float Sigma { get; } /// - /// Gets the horizontal gradient operator. - /// - public DenseMatrix KernelX { get; } - - /// - /// Gets the vertical gradient operator. + /// Gets the radius defining the size of the area to sample. /// - public DenseMatrix KernelY { get; } + public int Radius { get; } - /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - => new Convolution2PassProcessor(this.KernelX, this.KernelY, false).Apply(source, sourceRectangle, configuration); - - /// - /// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function - /// - /// The - private DenseMatrix CreateGaussianKernel() + public IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel { - int size = this.kernelSize; - float weight = this.Sigma; - var kernel = new DenseMatrix(size, 1); - - float sum = 0; - - float midpoint = (size - 1) / 2F; - for (int i = 0; i < size; i++) - { - float x = i - midpoint; - float gx = ImageMaths.Gaussian(x, weight); - sum += gx; - kernel[0, i] = gx; - } - - // Invert the kernel for sharpening. - int midpointRounded = (int)midpoint; - for (int i = 0; i < size; i++) - { - if (i == midpointRounded) - { - // Calculate central value - kernel[0, i] = (2F * sum) - kernel[0, i]; - } - else - { - // invert value - kernel[0, i] = -kernel[0, i]; - } - } - - // Normalize kernel so that the sum of all weights equals 1 - for (int i = 0; i < size; i++) - { - kernel[0, i] /= sum; - } - - return kernel; + return new GaussianSharpenProcessor(this); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs new file mode 100644 index 0000000000..edde9f9e7f --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs @@ -0,0 +1,42 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Primitives; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Convolution +{ + /// + /// Applies Gaussian sharpening processing to the image. + /// + /// The pixel format. + internal class GaussianSharpenProcessor : ImageProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + /// The defining the processor parameters. + public GaussianSharpenProcessor(GaussianSharpenProcessor definition) + { + int kernelSize = (definition.Radius * 2) + 1; + this.KernelX = ConvolutionProcessorHelpers.CreateGaussianSharpenKernel(kernelSize, definition.Sigma); + this.KernelY = this.KernelX.Transpose(); + } + + /// + /// Gets the horizontal gradient operator. + /// + public DenseMatrix KernelX { get; } + + /// + /// Gets the vertical gradient operator. + /// + public DenseMatrix KernelY { get; } + + /// + protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + => new Convolution2PassProcessor(this.KernelX, this.KernelY, false).Apply(source, sourceRectangle, configuration); + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Convolution/GaussianSharpenTest.cs b/tests/ImageSharp.Tests/Processing/Convolution/GaussianSharpenTest.cs index 675498745e..7c2205f4e4 100644 --- a/tests/ImageSharp.Tests/Processing/Convolution/GaussianSharpenTest.cs +++ b/tests/ImageSharp.Tests/Processing/Convolution/GaussianSharpenTest.cs @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Convolution public void GaussianSharpen_GaussianSharpenProcessorDefaultsSet() { this.operations.GaussianSharpen(); - var processor = this.Verify>(); + var processor = this.Verify(); Assert.Equal(3f, processor.Sigma); } @@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Convolution public void GaussianSharpen_amount_GaussianSharpenProcessorDefaultsSet() { this.operations.GaussianSharpen(0.2f); - var processor = this.Verify>(); + var processor = this.Verify(); Assert.Equal(.2f, processor.Sigma); } @@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Convolution public void GaussianSharpen_amount_rect_GaussianSharpenProcessorDefaultsSet() { this.operations.GaussianSharpen(0.6f, this.rect); - var processor = this.Verify>(this.rect); + var processor = this.Verify(this.rect); Assert.Equal(.6f, processor.Sigma); } From 91d0f6fd02126b9bc952a749f9eac2d3cf75b425 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 5 May 2019 00:18:23 +0200 Subject: [PATCH 127/223] drop IEdgeDetectorProcessor --- .../Processing/DetectEdgesExtensions.cs | 9 +++--- .../Convolution/BoxBlurProcessor{TPixel}.cs | 5 ++-- .../ConvolutionProcessorHelpers.cs | 8 +++--- .../Convolution/EdgeDetector2DProcessor.cs | 2 +- .../EdgeDetectorCompassProcessor.cs | 2 +- .../Convolution/EdgeDetectorProcessor.cs | 2 +- .../Convolution/GaussianSharpenProcessor.cs | 1 + .../Convolution/IEdgeDetectorProcessor.cs | 28 ------------------- .../Processors/IImageProcessor{TPixel}.cs | 4 +-- .../Processors/ImageProcessorExtensions.cs | 4 +-- .../Transforms/Resize/ResizeProcessor.cs | 2 +- .../Processing/Convolution/DetectEdgesTest.cs | 4 +-- 12 files changed, 22 insertions(+), 49 deletions(-) delete mode 100644 src/ImageSharp/Processing/Processors/Convolution/IEdgeDetectorProcessor.cs diff --git a/src/ImageSharp/Processing/DetectEdgesExtensions.cs b/src/ImageSharp/Processing/DetectEdgesExtensions.cs index 5ac89df291..a3be298d0b 100644 --- a/src/ImageSharp/Processing/DetectEdgesExtensions.cs +++ b/src/ImageSharp/Processing/DetectEdgesExtensions.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; using SixLabors.ImageSharp.Processing.Processors.Convolution; using SixLabors.Primitives; @@ -82,7 +83,7 @@ namespace SixLabors.ImageSharp.Processing /// The image this method extends. /// The filter for detecting edges. /// The . - public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, IEdgeDetectorProcessor filter) + private static IImageProcessingContext DetectEdges(this IImageProcessingContext source, IImageProcessor filter) where TPixel : struct, IPixel { return source.ApplyProcessor(filter); @@ -98,17 +99,17 @@ namespace SixLabors.ImageSharp.Processing /// /// The filter for detecting edges. /// The . - public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, Rectangle rectangle, IEdgeDetectorProcessor filter) + private static IImageProcessingContext DetectEdges(this IImageProcessingContext source, Rectangle rectangle, IImageProcessor filter) where TPixel : struct, IPixel { source.ApplyProcessor(filter, rectangle); return source; } - private static IEdgeDetectorProcessor GetProcessor(EdgeDetectionOperators filter, bool grayscale) + private static IImageProcessor GetProcessor(EdgeDetectionOperators filter, bool grayscale) where TPixel : struct, IPixel { - IEdgeDetectorProcessor processor; + IImageProcessor processor; switch (filter) { diff --git a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs index ba092cb424..f4252e840d 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs @@ -1,5 +1,5 @@ -// // Copyright (c) Six Labors and contributors. -// // Licensed under the Apache License, Version 2.0. +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; @@ -38,7 +38,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// public DenseMatrix KernelY { get; } - /// protected override void OnFrameApply( ImageFrame source, diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs index bb0072b82e..1fb26c976a 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs @@ -1,5 +1,5 @@ -// // Copyright (c) Six Labors and contributors. -// // Licensed under the Apache License, Version 2.0. +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. using System; @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// /// Kernel radius is calculated using the minimum viable value. - /// . + /// See http://chemaguerra.com/gaussian-filter-radius/ . /// internal static int GetDefaultGaussianRadius(float sigma) { @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution } /// - /// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function + /// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function. /// /// The . internal static DenseMatrix CreateGaussianBlurKernel(int size, float weight) diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs index b4ac89139a..ef185a01f0 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Defines a processor that detects edges within an image using two one-dimensional matrices. /// /// The pixel format. - internal abstract class EdgeDetector2DProcessor : ImageProcessor, IEdgeDetectorProcessor + internal abstract class EdgeDetector2DProcessor : ImageProcessor where TPixel : struct, IPixel { /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs index aa33699f5d..1205b02dc0 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Defines a processor that detects edges within an image using a eight two dimensional matrices. /// /// The pixel format. - internal abstract class EdgeDetectorCompassProcessor : ImageProcessor, IEdgeDetectorProcessor + internal abstract class EdgeDetectorCompassProcessor : ImageProcessor where TPixel : struct, IPixel { /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor.cs index 9ec79da9eb..b4d4763d03 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Defines a processor that detects edges within an image using a single two dimensional matrix. /// /// The pixel format. - internal abstract class EdgeDetectorProcessor : ImageProcessor, IEdgeDetectorProcessor + internal abstract class EdgeDetectorProcessor : ImageProcessor where TPixel : struct, IPixel { /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs index bc06ee6182..84e56869d3 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs @@ -70,6 +70,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// public int Radius { get; } + /// public IImageProcessor CreatePixelSpecificProcessor() where TPixel : struct, IPixel { diff --git a/src/ImageSharp/Processing/Processors/Convolution/IEdgeDetectorProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/IEdgeDetectorProcessor.cs deleted file mode 100644 index b2ecbf1158..0000000000 --- a/src/ImageSharp/Processing/Processors/Convolution/IEdgeDetectorProcessor.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing.Processors; - -namespace SixLabors.ImageSharp.Processing.Processors.Convolution -{ - /// - /// Provides properties and methods allowing the detection of edges within an image. - /// - /// The pixel format. - public interface IEdgeDetectorProcessor : IImageProcessor, IEdgeDetectorProcessor - where TPixel : struct, IPixel - { - } - - /// - /// Provides properties and methods allowing the detection of edges within an image. - /// - public interface IEdgeDetectorProcessor - { - /// - /// Gets a value indicating whether to convert the image to grayscale before performing edge detection. - /// - bool Grayscale { get; } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/IImageProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/IImageProcessor{TPixel}.cs index f2c7ef29c1..90dfaf8db5 100644 --- a/src/ImageSharp/Processing/Processors/IImageProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/IImageProcessor{TPixel}.cs @@ -1,5 +1,5 @@ -// // Copyright (c) Six Labors and contributors. -// // Licensed under the Apache License, Version 2.0. +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; diff --git a/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs b/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs index 1e189b9c78..762d761c6c 100644 --- a/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs +++ b/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs @@ -1,5 +1,5 @@ -// // Copyright (c) Six Labors and contributors. -// // Licensed under the Apache License, Version 2.0. +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs index 5710c4bd8c..8762d6b263 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs @@ -9,7 +9,7 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { /// - /// Implements resizing of images using various resamplers. + /// Defines an image resizing operation with the given and dimensional parameters. /// public class ResizeProcessor : IImageProcessor { diff --git a/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs b/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs index 60fa19b490..cf5feac275 100644 --- a/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs +++ b/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs @@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Convolution [Theory] [MemberData(nameof(EdgeDetectionTheoryData))] public void DetectEdges_filter_SobelProcessorDefaultsSet(TestType type, EdgeDetectionOperators filter) - where TProcessor : IEdgeDetectorProcessor + where TProcessor : IImageProcessor { this.operations.DetectEdges(filter); @@ -62,7 +62,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Convolution [Theory] [MemberData(nameof(EdgeDetectionTheoryData))] public void DetectEdges_filter_grayscale_SobelProcessorDefaultsSet(TestType type, EdgeDetectionOperators filter) - where TProcessor : IEdgeDetectorProcessor + where TProcessor : IImageProcessor { bool grey = (int)filter % 2 == 0; this.operations.DetectEdges(filter, grey); From 9b451fab0cfeb017c58a7d475565538b63942a00 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 5 May 2019 01:10:24 +0200 Subject: [PATCH 128/223] Refactor edge detection --- src/ImageSharp/ImageSharp.csproj.DotSettings | 1 + .../Processing/DetectEdgesExtensions.cs | 96 +++++++++---------- .../ConvolutionProcessorHelpers.cs | 2 +- .../Convolution/EdgeDetector2DProcessor.cs | 5 +- .../EdgeDetectorCompassProcessor.cs | 54 ++--------- .../Convolution/EdgeDetectorProcessor.cs | 37 ++----- .../EdgeDetectorProcessor{TPixel}.cs | 49 ++++++++++ .../Convolution/KayyaliProcessor.cs | 22 +++-- .../Convolution/Kernels/CompassKernels.cs | 57 +++++++++++ .../{ => Kernels}/KayyaliKernels.cs | 0 .../{ => Kernels}/KirschKernels.cs | 18 ++-- .../{ => Kernels}/LaplacianKernelFactory.cs | 0 .../{ => Kernels}/LaplacianKernels.cs | 0 .../{ => Kernels}/PrewittKernels.cs | 0 .../{ => Kernels}/RobertsCrossKernels.cs | 0 .../{ => Kernels}/RobinsonKernels.cs | 20 ++-- .../{ => Kernels}/ScharrKernels.cs | 0 .../Convolution/{ => Kernels}/SobelKernels.cs | 0 .../Processors/Convolution/KirschProcessor.cs | 40 ++------ .../Convolution/Laplacian3x3Processor.cs | 14 ++- .../Convolution/Laplacian5x5Processor.cs | 20 ++-- .../LaplacianOfGaussianProcessor.cs | 18 ++-- .../Convolution/PrewittProcessor.cs | 20 ++-- .../Convolution/RobertsCrossProcessor.cs | 18 ++-- .../Convolution/RobinsonProcessor.cs | 38 ++------ .../Processors/Convolution/ScharrProcessor.cs | 16 ++-- .../Processors/Convolution/SobelProcessor.cs | 27 ++++-- .../Transforms/EntropyCropProcessor.cs | 2 +- .../Processing/Convolution/DetectEdgesTest.cs | 40 ++++---- 29 files changed, 329 insertions(+), 285 deletions(-) create mode 100644 src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs create mode 100644 src/ImageSharp/Processing/Processors/Convolution/Kernels/CompassKernels.cs rename src/ImageSharp/Processing/Processors/Convolution/{ => Kernels}/KayyaliKernels.cs (100%) rename src/ImageSharp/Processing/Processors/Convolution/{ => Kernels}/KirschKernels.cs (81%) rename src/ImageSharp/Processing/Processors/Convolution/{ => Kernels}/LaplacianKernelFactory.cs (100%) rename src/ImageSharp/Processing/Processors/Convolution/{ => Kernels}/LaplacianKernels.cs (100%) rename src/ImageSharp/Processing/Processors/Convolution/{ => Kernels}/PrewittKernels.cs (100%) rename src/ImageSharp/Processing/Processors/Convolution/{ => Kernels}/RobertsCrossKernels.cs (100%) rename src/ImageSharp/Processing/Processors/Convolution/{ => Kernels}/RobinsonKernels.cs (78%) rename src/ImageSharp/Processing/Processors/Convolution/{ => Kernels}/ScharrKernels.cs (100%) rename src/ImageSharp/Processing/Processors/Convolution/{ => Kernels}/SobelKernels.cs (100%) diff --git a/src/ImageSharp/ImageSharp.csproj.DotSettings b/src/ImageSharp/ImageSharp.csproj.DotSettings index a7337240a5..e446893e94 100644 --- a/src/ImageSharp/ImageSharp.csproj.DotSettings +++ b/src/ImageSharp/ImageSharp.csproj.DotSettings @@ -6,5 +6,6 @@ True True True + True True True \ No newline at end of file diff --git a/src/ImageSharp/Processing/DetectEdgesExtensions.cs b/src/ImageSharp/Processing/DetectEdgesExtensions.cs index a3be298d0b..7527d601df 100644 --- a/src/ImageSharp/Processing/DetectEdgesExtensions.cs +++ b/src/ImageSharp/Processing/DetectEdgesExtensions.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors; using SixLabors.ImageSharp.Processing.Processors.Convolution; using SixLabors.Primitives; @@ -9,82 +8,79 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds edge detection extensions to the type. + /// Adds edge detection extensions to the type. /// public static class DetectEdgesExtensions { /// - /// Detects any edges within the image. Uses the filter + /// Detects any edges within the image. Uses the filter /// operating in grayscale mode. /// - /// The pixel format. /// The image this method extends. - /// The . - public static IImageProcessingContext DetectEdges(this IImageProcessingContext source) - where TPixel : struct, IPixel - => DetectEdges(source, new SobelProcessor(true)); + /// The . + public static IImageProcessingContext DetectEdges(this IImageProcessingContext source) => + DetectEdges(source, new SobelProcessor(true)); /// - /// Detects any edges within the image. Uses the filter + /// Detects any edges within the image. Uses the filter /// operating in grayscale mode. /// - /// The pixel format. /// The image this method extends. /// /// The structure that specifies the portion of the image object to alter. /// - /// The . - public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, Rectangle rectangle) - where TPixel : struct, IPixel - => DetectEdges(source, rectangle, new SobelProcessor(true)); + /// The . + public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, Rectangle rectangle) => + DetectEdges(source, rectangle, new SobelProcessor(true)); /// /// Detects any edges within the image. /// - /// The pixel format. /// The image this method extends. /// The filter for detecting edges. - /// The . - public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, EdgeDetectionOperators filter) - where TPixel : struct, IPixel - => DetectEdges(source, GetProcessor(filter, true)); + /// The . + public static IImageProcessingContext DetectEdges( + this IImageProcessingContext source, + EdgeDetectionOperators filter) => + DetectEdges(source, GetProcessor(filter, true)); /// /// Detects any edges within the image. /// - /// The pixel format. /// The image this method extends. /// The filter for detecting edges. /// Whether to convert the image to grayscale first. Defaults to true. - /// The . - public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, EdgeDetectionOperators filter, bool grayscale) - where TPixel : struct, IPixel - => DetectEdges(source, GetProcessor(filter, grayscale)); + /// The . + public static IImageProcessingContext DetectEdges( + this IImageProcessingContext source, + EdgeDetectionOperators filter, + bool grayscale) => + DetectEdges(source, GetProcessor(filter, grayscale)); /// /// Detects any edges within the image. /// - /// The pixel format. /// The image this method extends. /// The filter for detecting edges. /// /// The structure that specifies the portion of the image object to alter. /// /// Whether to convert the image to grayscale first. Defaults to true. - /// The . - public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, EdgeDetectionOperators filter, Rectangle rectangle, bool grayscale = true) - where TPixel : struct, IPixel - => DetectEdges(source, rectangle, GetProcessor(filter, grayscale)); + /// The . + public static IImageProcessingContext DetectEdges( + this IImageProcessingContext source, + EdgeDetectionOperators filter, + Rectangle rectangle, + bool grayscale = true) => + DetectEdges(source, rectangle, GetProcessor(filter, grayscale)); /// /// Detects any edges within the image. /// - /// The pixel format. /// The image this method extends. /// The filter for detecting edges. - /// The . - private static IImageProcessingContext DetectEdges(this IImageProcessingContext source, IImageProcessor filter) - where TPixel : struct, IPixel + /// The . + private static IImageProcessingContext DetectEdges(this IImageProcessingContext source, IImageProcessor filter) { return source.ApplyProcessor(filter); } @@ -92,65 +88,65 @@ namespace SixLabors.ImageSharp.Processing /// /// Detects any edges within the image. /// - /// The pixel format. /// The image this method extends. /// /// The structure that specifies the portion of the image object to alter. /// /// The filter for detecting edges. - /// The . - private static IImageProcessingContext DetectEdges(this IImageProcessingContext source, Rectangle rectangle, IImageProcessor filter) - where TPixel : struct, IPixel + /// The . + private static IImageProcessingContext DetectEdges( + this IImageProcessingContext source, + Rectangle rectangle, + IImageProcessor filter) { source.ApplyProcessor(filter, rectangle); return source; } - private static IImageProcessor GetProcessor(EdgeDetectionOperators filter, bool grayscale) - where TPixel : struct, IPixel + private static IImageProcessor GetProcessor(EdgeDetectionOperators filter, bool grayscale) { - IImageProcessor processor; + IImageProcessor processor; switch (filter) { case EdgeDetectionOperators.Kayyali: - processor = new KayyaliProcessor(grayscale); + processor = new KayyaliProcessor(grayscale); break; case EdgeDetectionOperators.Kirsch: - processor = new KirschProcessor(grayscale); + processor = new KirschProcessor(grayscale); break; case EdgeDetectionOperators.Laplacian3x3: - processor = new Laplacian3x3Processor(grayscale); + processor = new Laplacian3x3Processor(grayscale); break; case EdgeDetectionOperators.Laplacian5x5: - processor = new Laplacian5x5Processor(grayscale); + processor = new Laplacian5x5Processor(grayscale); break; case EdgeDetectionOperators.LaplacianOfGaussian: - processor = new LaplacianOfGaussianProcessor(grayscale); + processor = new LaplacianOfGaussianProcessor(grayscale); break; case EdgeDetectionOperators.Prewitt: - processor = new PrewittProcessor(grayscale); + processor = new PrewittProcessor(grayscale); break; case EdgeDetectionOperators.RobertsCross: - processor = new RobertsCrossProcessor(grayscale); + processor = new RobertsCrossProcessor(grayscale); break; case EdgeDetectionOperators.Robinson: - processor = new RobinsonProcessor(grayscale); + processor = new RobinsonProcessor(grayscale); break; case EdgeDetectionOperators.Scharr: - processor = new ScharrProcessor(grayscale); + processor = new ScharrProcessor(grayscale); break; default: - processor = new SobelProcessor(grayscale); + processor = new SobelProcessor(grayscale); break; } diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs index 1fb26c976a..661ab523db 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// /// Kernel radius is calculated using the minimum viable value. - /// See http://chemaguerra.com/gaussian-filter-radius/ . + /// See . /// internal static int GetDefaultGaussianRadius(float sigma) { diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs index ef185a01f0..5daf14fc32 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Defines a processor that detects edges within an image using two one-dimensional matrices. /// /// The pixel format. - internal abstract class EdgeDetector2DProcessor : ImageProcessor + internal class EdgeDetector2DProcessor : ImageProcessor where TPixel : struct, IPixel { /// @@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// The horizontal gradient operator. /// The vertical gradient operator. /// Whether to convert the image to grayscale before performing edge detection. - protected EdgeDetector2DProcessor(in DenseMatrix kernelX, in DenseMatrix kernelY, bool grayscale) + internal EdgeDetector2DProcessor(in DenseMatrix kernelX, in DenseMatrix kernelY, bool grayscale) { Guard.IsTrue(kernelX.Size.Equals(kernelY.Size), $"{nameof(kernelX)} {nameof(kernelY)}", "Kernel sizes must be the same."); this.KernelX = kernelX; @@ -39,7 +39,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// public DenseMatrix KernelY { get; } - /// public bool Grayscale { get; } /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs index 1205b02dc0..227003195d 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs @@ -19,57 +19,23 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Defines a processor that detects edges within an image using a eight two dimensional matrices. /// /// The pixel format. - internal abstract class EdgeDetectorCompassProcessor : ImageProcessor + internal class EdgeDetectorCompassProcessor : ImageProcessor where TPixel : struct, IPixel { /// /// Initializes a new instance of the class. /// + /// Gets the kernels to use. /// Whether to convert the image to grayscale before performing edge detection. - protected EdgeDetectorCompassProcessor(bool grayscale) => this.Grayscale = grayscale; - - /// - /// Gets the North gradient operator - /// - public abstract DenseMatrix North { get; } - - /// - /// Gets the NorthWest gradient operator - /// - public abstract DenseMatrix NorthWest { get; } - - /// - /// Gets the West gradient operator - /// - public abstract DenseMatrix West { get; } - - /// - /// Gets the SouthWest gradient operator - /// - public abstract DenseMatrix SouthWest { get; } - - /// - /// Gets the South gradient operator - /// - public abstract DenseMatrix South { get; } - - /// - /// Gets the SouthEast gradient operator - /// - public abstract DenseMatrix SouthEast { get; } - - /// - /// Gets the East gradient operator - /// - public abstract DenseMatrix East { get; } + internal EdgeDetectorCompassProcessor(CompassKernels kernels, bool grayscale) + { + this.Grayscale = grayscale; + this.Kernels = kernels; + } - /// - /// Gets the NorthEast gradient operator - /// - public abstract DenseMatrix NorthEast { get; } + private CompassKernels Kernels { get; } - /// - public bool Grayscale { get; } + private bool Grayscale { get; } /// protected override void BeforeFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) @@ -83,7 +49,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - DenseMatrix[] kernels = { this.North, this.NorthWest, this.West, this.SouthWest, this.South, this.SouthEast, this.East, this.NorthEast }; + DenseMatrix[] kernels = this.Kernels.Flatten(); int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor.cs index b4d4763d03..75be15bcc7 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor.cs @@ -2,49 +2,30 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; -using SixLabors.ImageSharp.Processing.Processors.Filters; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// /// Defines a processor that detects edges within an image using a single two dimensional matrix. /// - /// The pixel format. - internal abstract class EdgeDetectorProcessor : ImageProcessor - where TPixel : struct, IPixel + public abstract class EdgeDetectorProcessor : IImageProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// The 2d gradient operator. - /// Whether to convert the image to grayscale before performing edge detection. - protected EdgeDetectorProcessor(in DenseMatrix kernelXY, bool grayscale) + /// A value indicating whether to convert the image to grayscale before performing edge detection. + protected EdgeDetectorProcessor(bool grayscale) { - this.KernelXY = kernelXY; this.Grayscale = grayscale; } - /// - public bool Grayscale { get; } - /// - /// Gets the 2d gradient operator. + /// Gets a value indicating whether to convert the image to grayscale before performing edge detection. /// - public DenseMatrix KernelXY { get; } - - /// - protected override void BeforeFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - { - if (this.Grayscale) - { - new GrayscaleBt709Processor(1F).ApplyToFrame(source, sourceRectangle, configuration); - } - } + public bool Grayscale { get; } - /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - => new ConvolutionProcessor(this.KernelXY, true).Apply(source, sourceRectangle, configuration); + /// + public abstract IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel; } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs new file mode 100644 index 0000000000..026313cc12 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs @@ -0,0 +1,49 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Primitives; +using SixLabors.ImageSharp.Processing.Processors.Filters; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Convolution +{ + /// + /// Defines a processor that detects edges within an image using a single two dimensional matrix. + /// + /// The pixel format. + internal class EdgeDetectorProcessor : ImageProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + /// The 2d gradient operator. + /// Whether to convert the image to grayscale before performing edge detection. + public EdgeDetectorProcessor(in DenseMatrix kernelXY, bool grayscale) + { + this.KernelXY = kernelXY; + this.Grayscale = grayscale; + } + + public bool Grayscale { get; } + + /// + /// Gets the 2d gradient operator. + /// + public DenseMatrix KernelXY { get; } + + /// + protected override void BeforeFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + if (this.Grayscale) + { + new GrayscaleBt709Processor(1F).ApplyToFrame(source, sourceRectangle, configuration); + } + } + + /// + protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + => new ConvolutionProcessor(this.KernelXY, true).Apply(source, sourceRectangle, configuration); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/KayyaliProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/KayyaliProcessor.cs index 8652efa120..99dfb17ffb 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/KayyaliProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/KayyaliProcessor.cs @@ -1,24 +1,30 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// - /// Applies edge detection processing to the image using the Kayyali operator filter. + /// Defines edge detection processing using the Kayyali operator filter. + /// See . /// - /// The pixel format. - internal class KayyaliProcessor : EdgeDetector2DProcessor - where TPixel : struct, IPixel + public class KayyaliProcessor : EdgeDetectorProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Whether to convert the image to grayscale before performing edge detection. public KayyaliProcessor(bool grayscale) - : base(KayyaliKernels.KayyaliX, KayyaliKernels.KayyaliY, grayscale) + : base(grayscale) + { + } + + /// + public override IImageProcessor CreatePixelSpecificProcessor() { + return new EdgeDetector2DProcessor( + KayyaliKernels.KayyaliX, + KayyaliKernels.KayyaliY, + this.Grayscale); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/Kernels/CompassKernels.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/CompassKernels.cs new file mode 100644 index 0000000000..f44de9105b --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Convolution/Kernels/CompassKernels.cs @@ -0,0 +1,57 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Convolution +{ + internal abstract class CompassKernels + { + /// + /// Gets the North gradient operator. + /// + public abstract DenseMatrix North { get; } + + /// + /// Gets the NorthWest gradient operator. + /// + public abstract DenseMatrix NorthWest { get; } + + /// + /// Gets the West gradient operator. + /// + public abstract DenseMatrix West { get; } + + /// + /// Gets the SouthWest gradient operator. + /// + public abstract DenseMatrix SouthWest { get; } + + /// + /// Gets the South gradient operator. + /// + public abstract DenseMatrix South { get; } + + /// + /// Gets the SouthEast gradient operator. + /// + public abstract DenseMatrix SouthEast { get; } + + /// + /// Gets the East gradient operator. + /// + public abstract DenseMatrix East { get; } + + /// + /// Gets the NorthEast gradient operator. + /// + public abstract DenseMatrix NorthEast { get; } + + public DenseMatrix[] Flatten() => + new[] + { + this.North, this.NorthWest, this.West, this.SouthWest, + this.South, this.SouthEast, this.East, this.NorthEast + }; + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/KayyaliKernels.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/KayyaliKernels.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Convolution/KayyaliKernels.cs rename to src/ImageSharp/Processing/Processors/Convolution/Kernels/KayyaliKernels.cs diff --git a/src/ImageSharp/Processing/Processors/Convolution/KirschKernels.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/KirschKernels.cs similarity index 81% rename from src/ImageSharp/Processing/Processors/Convolution/KirschKernels.cs rename to src/ImageSharp/Processing/Processors/Convolution/Kernels/KirschKernels.cs index 86232e306a..882b87075b 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/KirschKernels.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Kernels/KirschKernels.cs @@ -8,12 +8,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Contains the eight matrices used for Kirsch edge detection /// - internal static class KirschKernels + internal class KirschKernels : CompassKernels { /// /// Gets the North gradient operator /// - public static DenseMatrix KirschNorth => + public override DenseMatrix North => new float[,] { { 5, 5, 5 }, @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Gets the NorthWest gradient operator /// - public static DenseMatrix KirschNorthWest => + public override DenseMatrix NorthWest => new float[,] { { 5, 5, -3 }, @@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Gets the West gradient operator /// - public static DenseMatrix KirschWest => + public override DenseMatrix West => new float[,] { { 5, -3, -3 }, @@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Gets the SouthWest gradient operator /// - public static DenseMatrix KirschSouthWest => + public override DenseMatrix SouthWest => new float[,] { { -3, -3, -3 }, @@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Gets the South gradient operator /// - public static DenseMatrix KirschSouth => + public override DenseMatrix South => new float[,] { { -3, -3, -3 }, @@ -68,7 +68,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Gets the SouthEast gradient operator /// - public static DenseMatrix KirschSouthEast => + public override DenseMatrix SouthEast => new float[,] { { -3, -3, -3 }, @@ -79,7 +79,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Gets the East gradient operator /// - public static DenseMatrix KirschEast => + public override DenseMatrix East => new float[,] { { -3, -3, 5 }, @@ -90,7 +90,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Gets the NorthEast gradient operator /// - public static DenseMatrix KirschNorthEast => + public override DenseMatrix NorthEast => new float[,] { { -3, 5, 5 }, diff --git a/src/ImageSharp/Processing/Processors/Convolution/LaplacianKernelFactory.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/LaplacianKernelFactory.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Convolution/LaplacianKernelFactory.cs rename to src/ImageSharp/Processing/Processors/Convolution/Kernels/LaplacianKernelFactory.cs diff --git a/src/ImageSharp/Processing/Processors/Convolution/LaplacianKernels.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/LaplacianKernels.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Convolution/LaplacianKernels.cs rename to src/ImageSharp/Processing/Processors/Convolution/Kernels/LaplacianKernels.cs diff --git a/src/ImageSharp/Processing/Processors/Convolution/PrewittKernels.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/PrewittKernels.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Convolution/PrewittKernels.cs rename to src/ImageSharp/Processing/Processors/Convolution/Kernels/PrewittKernels.cs diff --git a/src/ImageSharp/Processing/Processors/Convolution/RobertsCrossKernels.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/RobertsCrossKernels.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Convolution/RobertsCrossKernels.cs rename to src/ImageSharp/Processing/Processors/Convolution/Kernels/RobertsCrossKernels.cs diff --git a/src/ImageSharp/Processing/Processors/Convolution/RobinsonKernels.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/RobinsonKernels.cs similarity index 78% rename from src/ImageSharp/Processing/Processors/Convolution/RobinsonKernels.cs rename to src/ImageSharp/Processing/Processors/Convolution/Kernels/RobinsonKernels.cs index 4f47184e30..699d669ec9 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/RobinsonKernels.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Kernels/RobinsonKernels.cs @@ -6,14 +6,14 @@ using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// - /// Contains the kernels used for Robinson edge detection + /// Contains the kernels used for Robinson edge detection. /// - internal static class RobinsonKernels + internal class RobinsonKernels : CompassKernels { /// /// Gets the North gradient operator /// - public static DenseMatrix RobinsonNorth => + public override DenseMatrix North => new float[,] { { 1, 2, 1 }, @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Gets the NorthWest gradient operator /// - public static DenseMatrix RobinsonNorthWest => + public override DenseMatrix NorthWest => new float[,] { { 2, 1, 0 }, @@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Gets the West gradient operator /// - public static DenseMatrix RobinsonWest => + public override DenseMatrix West => new float[,] { { 1, 0, -1 }, @@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Gets the SouthWest gradient operator /// - public static DenseMatrix RobinsonSouthWest => + public override DenseMatrix SouthWest => new float[,] { { 0, -1, -2 }, @@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Gets the South gradient operator /// - public static DenseMatrix RobinsonSouth => + public override DenseMatrix South => new float[,] { { -1, -2, -1 }, @@ -68,7 +68,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Gets the SouthEast gradient operator /// - public static DenseMatrix RobinsonSouthEast => + public override DenseMatrix SouthEast => new float[,] { { -2, -1, 0 }, @@ -79,7 +79,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Gets the East gradient operator /// - public static DenseMatrix RobinsonEast => + public override DenseMatrix East => new float[,] { { -1, 0, 1 }, @@ -90,7 +90,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Gets the NorthEast gradient operator /// - public static DenseMatrix RobinsonNorthEast => + public override DenseMatrix NorthEast => new float[,] { { 0, 1, 2 }, diff --git a/src/ImageSharp/Processing/Processors/Convolution/ScharrKernels.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/ScharrKernels.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Convolution/ScharrKernels.cs rename to src/ImageSharp/Processing/Processors/Convolution/Kernels/ScharrKernels.cs diff --git a/src/ImageSharp/Processing/Processors/Convolution/SobelKernels.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/SobelKernels.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Convolution/SobelKernels.cs rename to src/ImageSharp/Processing/Processors/Convolution/Kernels/SobelKernels.cs diff --git a/src/ImageSharp/Processing/Processors/Convolution/KirschProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/KirschProcessor.cs index c3188676f3..fc73903c7d 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/KirschProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/KirschProcessor.cs @@ -1,20 +1,16 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// - /// Applies edge detection processing to the image using the Kirsch operator filter. + /// Defines edge detection using the Kirsch operator filter. + /// See . /// - /// The pixel format. - internal class KirschProcessor : EdgeDetectorCompassProcessor - where TPixel : struct, IPixel + public class KirschProcessor : EdgeDetectorProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Whether to convert the image to grayscale before performing edge detection. public KirschProcessor(bool grayscale) @@ -22,28 +18,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { } - /// - public override DenseMatrix North => KirschKernels.KirschNorth; - - /// - public override DenseMatrix NorthWest => KirschKernels.KirschNorthWest; - - /// - public override DenseMatrix West => KirschKernels.KirschWest; - - /// - public override DenseMatrix SouthWest => KirschKernels.KirschSouthWest; - - /// - public override DenseMatrix South => KirschKernels.KirschSouth; - - /// - public override DenseMatrix SouthEast => KirschKernels.KirschSouthEast; - - /// - public override DenseMatrix East => KirschKernels.KirschEast; - - /// - public override DenseMatrix NorthEast => KirschKernels.KirschNorthEast; + /// + public override IImageProcessor CreatePixelSpecificProcessor() + { + return new EdgeDetectorCompassProcessor(new KirschKernels(), this.Grayscale); + } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/Laplacian3x3Processor.cs b/src/ImageSharp/Processing/Processors/Convolution/Laplacian3x3Processor.cs index f498d374cc..18a53cb131 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Laplacian3x3Processor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Laplacian3x3Processor.cs @@ -9,17 +9,21 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Applies edge detection processing to the image using the Laplacian 3x3 operator filter. /// /// - /// The pixel format. - internal class Laplacian3x3Processor : EdgeDetectorProcessor - where TPixel : struct, IPixel + public class Laplacian3x3Processor : EdgeDetectorProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Whether to convert the image to grayscale before performing edge detection. public Laplacian3x3Processor(bool grayscale) - : base(LaplacianKernels.Laplacian3x3, grayscale) + : base(grayscale) { } + + /// + public override IImageProcessor CreatePixelSpecificProcessor() + { + return new EdgeDetectorProcessor(LaplacianKernels.Laplacian3x3, this.Grayscale); + } } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/Laplacian5x5Processor.cs b/src/ImageSharp/Processing/Processors/Convolution/Laplacian5x5Processor.cs index 558acf7b35..1c95ca4903 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Laplacian5x5Processor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Laplacian5x5Processor.cs @@ -1,25 +1,27 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// - /// Applies edge detection processing to the image using the Laplacian 5x5 operator filter. - /// + /// Defines edge detection processing using the Laplacian 5x5 operator filter. + /// . /// - /// The pixel format. - internal class Laplacian5x5Processor : EdgeDetectorProcessor - where TPixel : struct, IPixel + public class Laplacian5x5Processor : EdgeDetectorProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Whether to convert the image to grayscale before performing edge detection. public Laplacian5x5Processor(bool grayscale) - : base(LaplacianKernels.Laplacian5x5, grayscale) + : base(grayscale) + { + } + + /// + public override IImageProcessor CreatePixelSpecificProcessor() { + return new EdgeDetectorProcessor(LaplacianKernels.Laplacian5x5, this.Grayscale); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/LaplacianOfGaussianProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/LaplacianOfGaussianProcessor.cs index 6cc65dc587..d904d69b1b 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/LaplacianOfGaussianProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/LaplacianOfGaussianProcessor.cs @@ -1,25 +1,27 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// /// Applies edge detection processing to the image using the Laplacian of Gaussian operator filter. - /// + /// See . /// - /// The pixel format. - internal class LaplacianOfGaussianProcessor : EdgeDetectorProcessor - where TPixel : struct, IPixel + public class LaplacianOfGaussianProcessor : EdgeDetectorProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Whether to convert the image to grayscale before performing edge detection. public LaplacianOfGaussianProcessor(bool grayscale) - : base(LaplacianKernels.LaplacianOfGaussianXY, grayscale) + : base(grayscale) + { + } + + /// + public override IImageProcessor CreatePixelSpecificProcessor() { + return new EdgeDetectorProcessor(LaplacianKernels.LaplacianOfGaussianXY, this.Grayscale); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/PrewittProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/PrewittProcessor.cs index 75ef4dac62..939b1e0c52 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/PrewittProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/PrewittProcessor.cs @@ -1,25 +1,27 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// - /// Applies edge detection processing to the image using the Prewitt operator filter. - /// + /// Defines edge detection using the Prewitt operator filter. + /// See . /// - /// The pixel format. - internal class PrewittProcessor : EdgeDetector2DProcessor - where TPixel : struct, IPixel + public class PrewittProcessor : EdgeDetectorProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Whether to convert the image to grayscale before performing edge detection. public PrewittProcessor(bool grayscale) - : base(PrewittKernels.PrewittX, PrewittKernels.PrewittY, grayscale) + : base(grayscale) + { + } + + /// + public override IImageProcessor CreatePixelSpecificProcessor() { + return new EdgeDetector2DProcessor(PrewittKernels.PrewittX, PrewittKernels.PrewittY, this.Grayscale); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/RobertsCrossProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/RobertsCrossProcessor.cs index d685860f62..ff41b6c694 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/RobertsCrossProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/RobertsCrossProcessor.cs @@ -6,20 +6,24 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// - /// Applies edge detection processing to the image using the Roberts Cross operator filter. - /// + /// Defines edge detection processing using the Roberts Cross operator filter. + /// See . /// - /// The pixel format. - internal class RobertsCrossProcessor : EdgeDetector2DProcessor - where TPixel : struct, IPixel + public class RobertsCrossProcessor : EdgeDetectorProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Whether to convert the image to grayscale before performing edge detection. public RobertsCrossProcessor(bool grayscale) - : base(RobertsCrossKernels.RobertsCrossX, RobertsCrossKernels.RobertsCrossY, grayscale) + : base(grayscale) { } + + /// + public override IImageProcessor CreatePixelSpecificProcessor() + { + return new EdgeDetector2DProcessor(RobertsCrossKernels.RobertsCrossX, RobertsCrossKernels.RobertsCrossY, this.Grayscale); + } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/RobinsonProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/RobinsonProcessor.cs index 193c1008dd..603a81f676 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/RobinsonProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/RobinsonProcessor.cs @@ -7,15 +7,13 @@ using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// - /// Applies edge detection processing to the image using the Robinson operator filter. - /// + /// Defines edge detection using the Robinson operator filter. + /// See . /// - /// The pixel format. - internal class RobinsonProcessor : EdgeDetectorCompassProcessor - where TPixel : struct, IPixel + internal class RobinsonProcessor : EdgeDetectorProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Whether to convert the image to grayscale before performing edge detection. public RobinsonProcessor(bool grayscale) @@ -23,28 +21,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { } - /// - public override DenseMatrix North => RobinsonKernels.RobinsonNorth; - - /// - public override DenseMatrix NorthWest => RobinsonKernels.RobinsonNorthWest; - - /// - public override DenseMatrix West => RobinsonKernels.RobinsonWest; - - /// - public override DenseMatrix SouthWest => RobinsonKernels.RobinsonSouthWest; - - /// - public override DenseMatrix South => RobinsonKernels.RobinsonSouth; - - /// - public override DenseMatrix SouthEast => RobinsonKernels.RobinsonSouthEast; - - /// - public override DenseMatrix East => RobinsonKernels.RobinsonEast; - - /// - public override DenseMatrix NorthEast => RobinsonKernels.RobinsonNorthEast; + /// + public override IImageProcessor CreatePixelSpecificProcessor() + { + return new EdgeDetectorCompassProcessor(new RobinsonKernels(), this.Grayscale); + } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/ScharrProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/ScharrProcessor.cs index 79fc0e79fc..d23ba53384 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/ScharrProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/ScharrProcessor.cs @@ -6,20 +6,24 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// - /// Applies edge detection processing to the image using the Scharr operator filter. + /// Defines edge detection processing using the Scharr operator filter. /// /// - /// The pixel format. - internal class ScharrProcessor : EdgeDetector2DProcessor - where TPixel : struct, IPixel + public class ScharrProcessor : EdgeDetectorProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Whether to convert the image to grayscale before performing edge detection. public ScharrProcessor(bool grayscale) - : base(ScharrKernels.ScharrX, ScharrKernels.ScharrY, grayscale) + : base(grayscale) { } + + /// + public override IImageProcessor CreatePixelSpecificProcessor() + { + return new EdgeDetector2DProcessor(ScharrKernels.ScharrX, ScharrKernels.ScharrY, this.Grayscale); + } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/SobelProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/SobelProcessor.cs index 3ca53f6f0f..157bb62e50 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/SobelProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/SobelProcessor.cs @@ -2,24 +2,37 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// - /// The Sobel operator filter. - /// + /// Defines edge detection using the Sobel operator filter. + /// See . /// - /// The pixel format. - internal class SobelProcessor : EdgeDetector2DProcessor - where TPixel : struct, IPixel + public class SobelProcessor : EdgeDetectorProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Whether to convert the image to grayscale before performing edge detection. public SobelProcessor(bool grayscale) - : base(SobelKernels.SobelX, SobelKernels.SobelY, grayscale) + : base(grayscale) { } + + /// + public override IImageProcessor CreatePixelSpecificProcessor() + { + return new EdgeDetector2DProcessor(SobelKernels.SobelX, SobelKernels.SobelY, this.Grayscale); + } + + // TODO: Move this to an appropriate extension method if possible. + internal void ApplyToFrame(ImageFrame frame, Rectangle sourceRectangle, Configuration configuration) + where TPixel : struct, IPixel + { + var processorImpl = new EdgeDetector2DProcessor(SobelKernels.SobelX, SobelKernels.SobelY, this.Grayscale); + processorImpl.Apply(frame, sourceRectangle, configuration); + } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs index 6de717afd9..0e744dd963 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs @@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms Configuration configuration = source.GetConfiguration(); // Detect the edges. - new SobelProcessor(false).Apply(temp, sourceRectangle, configuration); + new SobelProcessor(false).ApplyToFrame(temp, sourceRectangle, configuration); // Apply threshold binarization filter. new BinaryThresholdProcessor(this.Threshold).Apply(temp, sourceRectangle, configuration); diff --git a/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs b/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs index cf5feac275..07b9b1b8c6 100644 --- a/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs +++ b/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs @@ -21,8 +21,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Convolution this.operations.DetectEdges(); // TODO: Enable once we have updated the images - // SobelProcessor processor = this.Verify>(); - // Assert.True(processor.Grayscale); + SobelProcessor processor = this.Verify(); + Assert.True(processor.Grayscale); } [Fact] @@ -31,45 +31,45 @@ namespace SixLabors.ImageSharp.Tests.Processing.Convolution this.operations.DetectEdges(this.rect); // TODO: Enable once we have updated the images - // SobelProcessor processor = this.Verify>(this.rect); - // Assert.True(processor.Grayscale); + SobelProcessor processor = this.Verify(this.rect); + Assert.True(processor.Grayscale); } public static IEnumerable EdgeDetectionTheoryData => new[] { - new object[]{ new TestType>(), EdgeDetectionOperators.Kayyali }, - new object[]{ new TestType>(), EdgeDetectionOperators.Kirsch }, - new object[]{ new TestType>(), EdgeDetectionOperators.Laplacian3x3 }, - new object[]{ new TestType>(), EdgeDetectionOperators.Laplacian5x5 }, - new object[]{ new TestType>(), EdgeDetectionOperators.LaplacianOfGaussian }, - new object[]{ new TestType>(), EdgeDetectionOperators.Prewitt }, - new object[]{ new TestType>(), EdgeDetectionOperators.RobertsCross }, - new object[]{ new TestType>(), EdgeDetectionOperators.Robinson }, - new object[]{ new TestType>(), EdgeDetectionOperators.Scharr }, - new object[]{ new TestType>(), EdgeDetectionOperators.Sobel }, + new object[]{ new TestType(), EdgeDetectionOperators.Kayyali }, + new object[]{ new TestType(), EdgeDetectionOperators.Kirsch }, + new object[]{ new TestType(), EdgeDetectionOperators.Laplacian3x3 }, + new object[]{ new TestType(), EdgeDetectionOperators.Laplacian5x5 }, + new object[]{ new TestType(), EdgeDetectionOperators.LaplacianOfGaussian }, + new object[]{ new TestType(), EdgeDetectionOperators.Prewitt }, + new object[]{ new TestType(), EdgeDetectionOperators.RobertsCross }, + new object[]{ new TestType(), EdgeDetectionOperators.Robinson }, + new object[]{ new TestType(), EdgeDetectionOperators.Scharr }, + new object[]{ new TestType(), EdgeDetectionOperators.Sobel }, }; [Theory] [MemberData(nameof(EdgeDetectionTheoryData))] public void DetectEdges_filter_SobelProcessorDefaultsSet(TestType type, EdgeDetectionOperators filter) - where TProcessor : IImageProcessor + where TProcessor : EdgeDetectorProcessor { this.operations.DetectEdges(filter); // TODO: Enable once we have updated the images - // var processor = this.Verify(); - // Assert.True(processor.Grayscale); + var processor = this.Verify(); + Assert.True(processor.Grayscale); } [Theory] [MemberData(nameof(EdgeDetectionTheoryData))] public void DetectEdges_filter_grayscale_SobelProcessorDefaultsSet(TestType type, EdgeDetectionOperators filter) - where TProcessor : IImageProcessor + where TProcessor : EdgeDetectorProcessor { bool grey = (int)filter % 2 == 0; this.operations.DetectEdges(filter, grey); // TODO: Enable once we have updated the images - // var processor = this.Verify() - // Assert.Equal(grey, processor.Grayscale); + var processor = this.Verify(); + Assert.Equal(grey, processor.Grayscale); } } } \ No newline at end of file From e53e0dc9c3da1e15c3c5861fc8ae892112a98b28 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 5 May 2019 01:36:44 +0200 Subject: [PATCH 129/223] refactor Effects processors --- .../Processing/OilPaintExtensions.cs | 31 ++--- .../Processing/PixelateExtensions.cs | 23 ++-- .../Processors/Dithering/ErrorDiffuserBase.cs | 6 +- .../Effects/OilPaintingProcessor.cs | 119 ++-------------- .../Effects/OilPaintingProcessor{TPixel}.cs | 129 ++++++++++++++++++ .../Processors/Effects/PixelateProcessor.cs | 91 +----------- .../Effects/PixelateProcessor{TPixel}.cs | 111 +++++++++++++++ .../Processing/Effects/OilPaintTest.cs | 8 +- .../Processing/Effects/PixelateTest.cs | 6 +- 9 files changed, 288 insertions(+), 236 deletions(-) create mode 100644 src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs create mode 100644 src/ImageSharp/Processing/Processors/Effects/PixelateProcessor{TPixel}.cs diff --git a/src/ImageSharp/Processing/OilPaintExtensions.cs b/src/ImageSharp/Processing/OilPaintExtensions.cs index b6fa4149a6..ee51b31fab 100644 --- a/src/ImageSharp/Processing/OilPaintExtensions.cs +++ b/src/ImageSharp/Processing/OilPaintExtensions.cs @@ -1,14 +1,13 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Effects; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds oil painting effect extensions to the type. + /// Adds oil painting effect extensions to the type. /// public static class OilPaintExtensions { @@ -16,43 +15,36 @@ namespace SixLabors.ImageSharp.Processing /// Alters the colors of the image recreating an oil painting effect with levels and brushSize /// set to 10 and 15 respectively. /// - /// The pixel format. /// The image this method extends. /// The . - public static IImageProcessingContext OilPaint(this IImageProcessingContext source) - where TPixel : struct, IPixel - => OilPaint(source, 10, 15); + public static IImageProcessingContext OilPaint(this IImageProcessingContext source) => OilPaint(source, 10, 15); /// /// Alters the colors of the image recreating an oil painting effect with levels and brushSize /// set to 10 and 15 respectively. /// - /// The pixel format. /// The image this method extends. /// /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext OilPaint(this IImageProcessingContext source, Rectangle rectangle) - where TPixel : struct, IPixel - => OilPaint(source, 10, 15, rectangle); + public static IImageProcessingContext OilPaint(this IImageProcessingContext source, Rectangle rectangle) => + OilPaint(source, 10, 15, rectangle); /// /// Alters the colors of the image recreating an oil painting effect. /// - /// The pixel format. /// The image this method extends. /// The number of intensity levels. Higher values result in a broader range of color intensities forming part of the result image. /// The number of neighboring pixels used in calculating each individual pixel value. /// The . - public static IImageProcessingContext OilPaint(this IImageProcessingContext source, int levels, int brushSize) - where TPixel : struct, IPixel - => source.ApplyProcessor(new OilPaintingProcessor(levels, brushSize)); + public static IImageProcessingContext + OilPaint(this IImageProcessingContext source, int levels, int brushSize) => + source.ApplyProcessor(new OilPaintingProcessor(levels, brushSize)); /// /// Alters the colors of the image recreating an oil painting effect. /// - /// The pixel format. /// The image this method extends. /// The number of intensity levels. Higher values result in a broader range of color intensities forming part of the result image. /// The number of neighboring pixels used in calculating each individual pixel value. @@ -60,8 +52,11 @@ namespace SixLabors.ImageSharp.Processing /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext OilPaint(this IImageProcessingContext source, int levels, int brushSize, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new OilPaintingProcessor(levels, brushSize), rectangle); + public static IImageProcessingContext OilPaint( + this IImageProcessingContext source, + int levels, + int brushSize, + Rectangle rectangle) => + source.ApplyProcessor(new OilPaintingProcessor(levels, brushSize), rectangle); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/PixelateExtensions.cs b/src/ImageSharp/Processing/PixelateExtensions.cs index 4507f63923..d9f7f9be50 100644 --- a/src/ImageSharp/Processing/PixelateExtensions.cs +++ b/src/ImageSharp/Processing/PixelateExtensions.cs @@ -1,50 +1,45 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Effects; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds pixelation effect extensions to the type. + /// Adds pixelation effect extensions to the type. /// public static class PixelateExtensions { /// /// Pixelates an image with the given pixel size. /// - /// The pixel format. /// The image this method extends. /// The . - public static IImageProcessingContext Pixelate(this IImageProcessingContext source) - where TPixel : struct, IPixel - => Pixelate(source, 4); + public static IImageProcessingContext Pixelate(this IImageProcessingContext source) => Pixelate(source, 4); /// /// Pixelates an image with the given pixel size. /// - /// The pixel format. /// The image this method extends. /// The size of the pixels. /// The . - public static IImageProcessingContext Pixelate(this IImageProcessingContext source, int size) - where TPixel : struct, IPixel - => source.ApplyProcessor(new PixelateProcessor(size)); + public static IImageProcessingContext Pixelate(this IImageProcessingContext source, int size) => + source.ApplyProcessor(new PixelateProcessor(size)); /// /// Pixelates an image with the given pixel size. /// - /// The pixel format. /// The image this method extends. /// The size of the pixels. /// /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext Pixelate(this IImageProcessingContext source, int size, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new PixelateProcessor(size), rectangle); + public static IImageProcessingContext Pixelate( + this IImageProcessingContext source, + int size, + Rectangle rectangle) => + source.ApplyProcessor(new PixelateProcessor(size), rectangle); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffuserBase.cs b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffuserBase.cs index abf5dce184..327828c39e 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffuserBase.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffuserBase.cs @@ -21,12 +21,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering private readonly Vector4 divisorVector; /// - /// The matrix width + /// The matrix width. /// private readonly int matrixHeight; /// - /// The matrix height + /// The matrix height. /// private readonly int matrixWidth; @@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering private readonly int startingOffset; /// - /// The diffusion matrix + /// The diffusion matrix. /// private readonly DenseMatrix matrix; diff --git a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs index 1b17c470ed..14e164fb77 100644 --- a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs @@ -1,27 +1,17 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; -using System.Numerics; - -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Effects { /// - /// Applies oil painting effect processing to the image. + /// Defines an oil painting effect. /// - /// Adapted from by Dewald Esterhuizen. - /// The pixel format. - internal class OilPaintingProcessor : ImageProcessor - where TPixel : struct, IPixel + public class OilPaintingProcessor : IImageProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The number of intensity levels. Higher values result in a broader range of color intensities forming part of the result image. @@ -39,111 +29,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects } /// - /// Gets the intensity levels + /// Gets the number of intensity levels. /// public int Levels { get; } /// - /// Gets the brush size + /// Gets the brush size. /// public int BrushSize { get; } - /// - protected override void OnFrameApply( - ImageFrame source, - Rectangle sourceRectangle, - Configuration configuration) + /// + public IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel { - if (this.BrushSize <= 0 || this.BrushSize > source.Height || this.BrushSize > source.Width) - { - throw new ArgumentOutOfRangeException(nameof(this.BrushSize)); - } - - int startY = sourceRectangle.Y; - int endY = sourceRectangle.Bottom; - int startX = sourceRectangle.X; - int endX = sourceRectangle.Right; - int maxY = endY - 1; - int maxX = endX - 1; - - int radius = this.BrushSize >> 1; - int levels = this.Levels; - - using (Buffer2D targetPixels = configuration.MemoryAllocator.Allocate2D(source.Size())) - { - source.CopyTo(targetPixels); - - var workingRect = Rectangle.FromLTRB(startX, startY, endX, endY); - ParallelHelper.IterateRows( - workingRect, - configuration, - rows => - { - for (int y = rows.Min; y < rows.Max; y++) - { - Span sourceRow = source.GetPixelRowSpan(y); - Span targetRow = targetPixels.GetRowSpan(y); - - for (int x = startX; x < endX; x++) - { - int maxIntensity = 0; - int maxIndex = 0; - - int[] intensityBin = new int[levels]; - float[] redBin = new float[levels]; - float[] blueBin = new float[levels]; - float[] greenBin = new float[levels]; - - for (int fy = 0; fy <= radius; fy++) - { - int fyr = fy - radius; - int offsetY = y + fyr; - - offsetY = offsetY.Clamp(0, maxY); - - Span sourceOffsetRow = source.GetPixelRowSpan(offsetY); - - for (int fx = 0; fx <= radius; fx++) - { - int fxr = fx - radius; - int offsetX = x + fxr; - offsetX = offsetX.Clamp(0, maxX); - - var vector = sourceOffsetRow[offsetX].ToVector4(); - - float sourceRed = vector.X; - float sourceBlue = vector.Z; - float sourceGreen = vector.Y; - - int currentIntensity = (int)MathF.Round( - (sourceBlue + sourceGreen + sourceRed) / 3F * (levels - 1)); - - intensityBin[currentIntensity]++; - blueBin[currentIntensity] += sourceBlue; - greenBin[currentIntensity] += sourceGreen; - redBin[currentIntensity] += sourceRed; - - if (intensityBin[currentIntensity] > maxIntensity) - { - maxIntensity = intensityBin[currentIntensity]; - maxIndex = currentIntensity; - } - } - - float red = MathF.Abs(redBin[maxIndex] / maxIntensity); - float green = MathF.Abs(greenBin[maxIndex] / maxIntensity); - float blue = MathF.Abs(blueBin[maxIndex] / maxIntensity); - - ref TPixel pixel = ref targetRow[x]; - pixel.FromVector4( - new Vector4(red, green, blue, sourceRow[x].ToVector4().W)); - } - } - } - }); - - Buffer2D.SwapOrCopyContent(source.PixelBuffer, targetPixels); - } + return new OilPaintingProcessor(this); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs new file mode 100644 index 0000000000..9775e5ca93 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs @@ -0,0 +1,129 @@ +// Copyright (c) Six Labors and contributors. +/// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Effects +{ + /// + /// Applies oil painting effect processing to the image. + /// + /// Adapted from by Dewald Esterhuizen. + /// The pixel format. + internal class OilPaintingProcessor : ImageProcessor + where TPixel : struct, IPixel + { + private readonly OilPaintingProcessor definition; + + public OilPaintingProcessor(OilPaintingProcessor definition) + { + this.definition = definition; + } + + /// + protected override void OnFrameApply( + ImageFrame source, + Rectangle sourceRectangle, + Configuration configuration) + { + int brushSize = this.definition.BrushSize; + if (brushSize <= 0 || brushSize > source.Height || brushSize > source.Width) + { + throw new ArgumentOutOfRangeException(nameof(brushSize)); + } + + int startY = sourceRectangle.Y; + int endY = sourceRectangle.Bottom; + int startX = sourceRectangle.X; + int endX = sourceRectangle.Right; + int maxY = endY - 1; + int maxX = endX - 1; + + int radius = brushSize >> 1; + int levels = this.definition.Levels; + + using (Buffer2D targetPixels = configuration.MemoryAllocator.Allocate2D(source.Size())) + { + source.CopyTo(targetPixels); + + var workingRect = Rectangle.FromLTRB(startX, startY, endX, endY); + ParallelHelper.IterateRows( + workingRect, + configuration, + rows => + { + for (int y = rows.Min; y < rows.Max; y++) + { + Span sourceRow = source.GetPixelRowSpan(y); + Span targetRow = targetPixels.GetRowSpan(y); + + for (int x = startX; x < endX; x++) + { + int maxIntensity = 0; + int maxIndex = 0; + + int[] intensityBin = new int[levels]; + float[] redBin = new float[levels]; + float[] blueBin = new float[levels]; + float[] greenBin = new float[levels]; + + for (int fy = 0; fy <= radius; fy++) + { + int fyr = fy - radius; + int offsetY = y + fyr; + + offsetY = offsetY.Clamp(0, maxY); + + Span sourceOffsetRow = source.GetPixelRowSpan(offsetY); + + for (int fx = 0; fx <= radius; fx++) + { + int fxr = fx - radius; + int offsetX = x + fxr; + offsetX = offsetX.Clamp(0, maxX); + + var vector = sourceOffsetRow[offsetX].ToVector4(); + + float sourceRed = vector.X; + float sourceBlue = vector.Z; + float sourceGreen = vector.Y; + + int currentIntensity = (int)MathF.Round( + (sourceBlue + sourceGreen + sourceRed) / 3F * (levels - 1)); + + intensityBin[currentIntensity]++; + blueBin[currentIntensity] += sourceBlue; + greenBin[currentIntensity] += sourceGreen; + redBin[currentIntensity] += sourceRed; + + if (intensityBin[currentIntensity] > maxIntensity) + { + maxIntensity = intensityBin[currentIntensity]; + maxIndex = currentIntensity; + } + } + + float red = MathF.Abs(redBin[maxIndex] / maxIntensity); + float green = MathF.Abs(greenBin[maxIndex] / maxIntensity); + float blue = MathF.Abs(blueBin[maxIndex] / maxIntensity); + + ref TPixel pixel = ref targetRow[x]; + pixel.FromVector4( + new Vector4(red, green, blue, sourceRow[x].ToVector4().W)); + } + } + } + }); + + Buffer2D.SwapOrCopyContent(source.PixelBuffer, targetPixels); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs index 50f76efed7..eeb2aafcb5 100644 --- a/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs @@ -1,25 +1,17 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Common; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Effects { /// - /// Applies a pixelation effect processing to the image. + /// Defines a pixelation effect of a given size. /// - /// The pixel format. - internal class PixelateProcessor : ImageProcessor - where TPixel : struct, IPixel + public class PixelateProcessor : IImageProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The size of the pixels. Must be greater than 0. /// @@ -36,80 +28,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects /// public int Size { get; } - /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + /// + public IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel { - if (this.Size <= 0 || this.Size > source.Height || this.Size > source.Width) - { - throw new ArgumentOutOfRangeException(nameof(this.Size)); - } - - int startY = sourceRectangle.Y; - int endY = sourceRectangle.Bottom; - int startX = sourceRectangle.X; - int endX = sourceRectangle.Right; - int size = this.Size; - int offset = this.Size / 2; - - // Align start/end positions. - int minX = Math.Max(0, startX); - int maxX = Math.Min(source.Width, endX); - int minY = Math.Max(0, startY); - int maxY = Math.Min(source.Height, endY); - - // Reset offset if necessary. - if (minX > 0) - { - startX = 0; - } - - if (minY > 0) - { - startY = 0; - } - - // Get the range on the y-plane to choose from. - IEnumerable range = EnumerableExtensions.SteppedRange(minY, i => i < maxY, size); - - Parallel.ForEach( - range, - configuration.GetParallelOptions(), - y => - { - int offsetY = y - startY; - int offsetPy = offset; - - // Make sure that the offset is within the boundary of the image. - while (offsetY + offsetPy >= maxY) - { - offsetPy--; - } - - Span row = source.GetPixelRowSpan(offsetY + offsetPy); - - for (int x = minX; x < maxX; x += size) - { - int offsetX = x - startX; - int offsetPx = offset; - - while (x + offsetPx >= maxX) - { - offsetPx--; - } - - // Get the pixel color in the centre of the soon to be pixelated area. - TPixel pixel = row[offsetX + offsetPx]; - - // For each pixel in the pixelate size, set it to the centre color. - for (int l = offsetY; l < offsetY + size && l < maxY; l++) - { - for (int k = offsetX; k < offsetX + size && k < maxX; k++) - { - source[k, l] = pixel; - } - } - } - }); + return new PixelateProcessor(this); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor{TPixel}.cs new file mode 100644 index 0000000000..21f3bb774d --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor{TPixel}.cs @@ -0,0 +1,111 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Common; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Effects +{ + /// + /// Applies a pixelation effect processing to the image. + /// + /// The pixel format. + internal class PixelateProcessor : ImageProcessor + where TPixel : struct, IPixel + { + private readonly PixelateProcessor definition; + + /// + /// Initializes a new instance of the class. + /// + /// The . + public PixelateProcessor(PixelateProcessor definition) + { + this.definition = definition; + } + + private int Size => this.definition.Size; + + /// + protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + if (this.Size <= 0 || this.Size > source.Height || this.Size > source.Width) + { + throw new ArgumentOutOfRangeException(nameof(this.Size)); + } + + int startY = sourceRectangle.Y; + int endY = sourceRectangle.Bottom; + int startX = sourceRectangle.X; + int endX = sourceRectangle.Right; + int size = this.Size; + int offset = this.Size / 2; + + // Align start/end positions. + int minX = Math.Max(0, startX); + int maxX = Math.Min(source.Width, endX); + int minY = Math.Max(0, startY); + int maxY = Math.Min(source.Height, endY); + + // Reset offset if necessary. + if (minX > 0) + { + startX = 0; + } + + if (minY > 0) + { + startY = 0; + } + + // Get the range on the y-plane to choose from. + IEnumerable range = EnumerableExtensions.SteppedRange(minY, i => i < maxY, size); + + Parallel.ForEach( + range, + configuration.GetParallelOptions(), + y => + { + int offsetY = y - startY; + int offsetPy = offset; + + // Make sure that the offset is within the boundary of the image. + while (offsetY + offsetPy >= maxY) + { + offsetPy--; + } + + Span row = source.GetPixelRowSpan(offsetY + offsetPy); + + for (int x = minX; x < maxX; x += size) + { + int offsetX = x - startX; + int offsetPx = offset; + + while (x + offsetPx >= maxX) + { + offsetPx--; + } + + // Get the pixel color in the centre of the soon to be pixelated area. + TPixel pixel = row[offsetX + offsetPx]; + + // For each pixel in the pixelate size, set it to the centre color. + for (int l = offsetY; l < offsetY + size && l < maxY; l++) + { + for (int k = offsetX; k < offsetX + size && k < maxX; k++) + { + source[k, l] = pixel; + } + } + } + }); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Effects/OilPaintTest.cs b/tests/ImageSharp.Tests/Processing/Effects/OilPaintTest.cs index 9cd24fc6d1..6e1ee40f56 100644 --- a/tests/ImageSharp.Tests/Processing/Effects/OilPaintTest.cs +++ b/tests/ImageSharp.Tests/Processing/Effects/OilPaintTest.cs @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Effects public void OilPaint_OilPaintingProcessorDefaultsSet() { this.operations.OilPaint(); - var processor = this.Verify>(); + var processor = this.Verify(); Assert.Equal(10, processor.Levels); Assert.Equal(15, processor.BrushSize); @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Effects public void OilPaint_rect_OilPaintingProcessorDefaultsSet() { this.operations.OilPaint(this.rect); - var processor = this.Verify>(this.rect); + var processor = this.Verify(this.rect); Assert.Equal(10, processor.Levels); Assert.Equal(15, processor.BrushSize); @@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Effects public void OilPaint_Levels_Brsuh_OilPaintingProcessorDefaultsSet() { this.operations.OilPaint(34, 65); - var processor = this.Verify>(); + var processor = this.Verify(); Assert.Equal(34, processor.Levels); Assert.Equal(65, processor.BrushSize); @@ -43,7 +43,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Effects public void OilPaint_Levels_Brsuh_rect_OilPaintingProcessorDefaultsSet() { this.operations.OilPaint(54, 43, this.rect); - var processor = this.Verify>(this.rect); + var processor = this.Verify(this.rect); Assert.Equal(54, processor.Levels); Assert.Equal(43, processor.BrushSize); diff --git a/tests/ImageSharp.Tests/Processing/Effects/PixelateTest.cs b/tests/ImageSharp.Tests/Processing/Effects/PixelateTest.cs index a93eaf0bc6..d8ce291707 100644 --- a/tests/ImageSharp.Tests/Processing/Effects/PixelateTest.cs +++ b/tests/ImageSharp.Tests/Processing/Effects/PixelateTest.cs @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Effects public void Pixelate_PixelateProcessorDefaultsSet() { this.operations.Pixelate(); - var processor = this.Verify>(); + var processor = this.Verify(); Assert.Equal(4, processor.Size); } @@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Effects public void Pixelate_Size_PixelateProcessorDefaultsSet() { this.operations.Pixelate(12); - var processor = this.Verify>(); + var processor = this.Verify(); Assert.Equal(12, processor.Size); } @@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Effects public void Pixelate_Size_rect_PixelateProcessorDefaultsSet() { this.operations.Pixelate(23, this.rect); - var processor = this.Verify>(this.rect); + var processor = this.Verify(this.rect); Assert.Equal(23, processor.Size); } From 3c7988cef6e14711661707ae3cccb81016b28161 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 5 May 2019 02:27:36 +0200 Subject: [PATCH 130/223] Finished refactoring transforms --- src/ImageSharp/Processing/CropExtensions.cs | 15 +- .../Processing/EntropyCropExtensions.cs | 19 +- src/ImageSharp/Processing/FlipExtensions.cs | 9 +- .../Effects/OilPaintingProcessor{TPixel}.cs | 2 +- .../Transforms/AffineTransformProcessor.cs | 106 +-------- .../AffineTransformProcessor{TPixel}.cs | 127 +++++++++++ .../Transforms/AutoOrientProcessor.cs | 92 +------- .../Transforms/AutoOrientProcessor{TPixel}.cs | 103 +++++++++ .../Processors/Transforms/CropProcessor.cs | 59 +---- .../Transforms/CropProcessor{TPixel}.cs | 75 ++++++ .../Transforms/EntropyCropProcessor.cs | 46 +--- .../EntropyCropProcessor{TPixel}.cs | 59 +++++ .../Processors/Transforms/FlipProcessor.cs | 76 +------ .../Transforms/FlipProcessor{TPixel}.cs | 87 +++++++ .../ProjectiveTransformProcessor.cs | 109 +-------- .../ProjectiveTransformProcessor{TPixel}.cs | 126 ++++++++++ .../Processors/Transforms/RotateProcessor.cs | 207 +---------------- .../Transforms/RotateProcessor{TPixel}.cs | 215 ++++++++++++++++++ .../Processors/Transforms/SkewProcessor.cs | 10 +- .../Transforms/TransformProcessorBase.cs | 2 +- .../Transforms/TransformProcessorHelpers.cs | 2 +- src/ImageSharp/Processing/RotateExtensions.cs | 24 +- .../Processing/RotateFlipExtensions.cs | 6 +- src/ImageSharp/Processing/SkewExtensions.cs | 24 +- .../Processing/TransformExtensions.cs | 97 ++++---- .../ImageSharp.Tests/Drawing/DrawImageTest.cs | 3 +- .../Processing/Transforms/CropTest.cs | 4 +- .../Processing/Transforms/EntropyCropTest.cs | 2 +- .../Processing/Transforms/FlipTests.cs | 2 +- .../Processing/Transforms/PadTest.cs | 8 +- .../Processing/Transforms/RotateFlipTests.cs | 4 +- .../Processing/Transforms/RotateTests.cs | 4 +- .../Processing/Transforms/SkewTest.cs | 2 +- .../Transforms/TransformsHelpersTest.cs | 2 +- 34 files changed, 953 insertions(+), 775 deletions(-) create mode 100644 src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs create mode 100644 src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs create mode 100644 src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs create mode 100644 src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs create mode 100644 src/ImageSharp/Processing/Processors/Transforms/FlipProcessor{TPixel}.cs create mode 100644 src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs create mode 100644 src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs diff --git a/src/ImageSharp/Processing/CropExtensions.cs b/src/ImageSharp/Processing/CropExtensions.cs index 1c0d80afc9..6aaff5656f 100644 --- a/src/ImageSharp/Processing/CropExtensions.cs +++ b/src/ImageSharp/Processing/CropExtensions.cs @@ -1,40 +1,35 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Transforms; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of cropping operations to the type. + /// Adds extensions that allow the application of cropping operations to the type. /// public static class CropExtensions { /// /// Crops an image to the given width and height. /// - /// The pixel format. /// The image to resize. /// The target image width. /// The target image height. /// The - public static IImageProcessingContext Crop(this IImageProcessingContext source, int width, int height) - where TPixel : struct, IPixel - => Crop(source, new Rectangle(0, 0, width, height)); + public static IImageProcessingContext Crop(this IImageProcessingContext source, int width, int height) => + Crop(source, new Rectangle(0, 0, width, height)); /// /// Crops an image to the given rectangle. /// - /// The pixel format. /// The image to crop. /// /// The structure that specifies the portion of the image object to retain. /// /// The - public static IImageProcessingContext Crop(this IImageProcessingContext source, Rectangle cropRectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new CropProcessor(cropRectangle, source.GetCurrentSize())); + public static IImageProcessingContext Crop(this IImageProcessingContext source, Rectangle cropRectangle) => + source.ApplyProcessor(new CropProcessor(cropRectangle, source.GetCurrentSize())); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/EntropyCropExtensions.cs b/src/ImageSharp/Processing/EntropyCropExtensions.cs index 157e69ef2a..34bc5daebf 100644 --- a/src/ImageSharp/Processing/EntropyCropExtensions.cs +++ b/src/ImageSharp/Processing/EntropyCropExtensions.cs @@ -1,35 +1,30 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Transforms; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of entropy cropping operations to the type. + /// Adds extensions that allow the application of entropy cropping operations to the type. /// public static class EntropyCropExtensions { /// /// Crops an image to the area of greatest entropy using a threshold for entropic density of .5F. /// - /// The pixel format. /// The image to crop. - /// The - public static IImageProcessingContext EntropyCrop(this IImageProcessingContext source) - where TPixel : struct, IPixel - => source.ApplyProcessor(new EntropyCropProcessor()); + /// The . + public static IImageProcessingContext EntropyCrop(this IImageProcessingContext source) => + source.ApplyProcessor(new EntropyCropProcessor()); /// /// Crops an image to the area of greatest entropy. /// - /// The pixel format. /// The image to crop. /// The threshold for entropic density. - /// The - public static IImageProcessingContext EntropyCrop(this IImageProcessingContext source, float threshold) - where TPixel : struct, IPixel - => source.ApplyProcessor(new EntropyCropProcessor(threshold)); + /// The . + public static IImageProcessingContext EntropyCrop(this IImageProcessingContext source, float threshold) => + source.ApplyProcessor(new EntropyCropProcessor(threshold)); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/FlipExtensions.cs b/src/ImageSharp/Processing/FlipExtensions.cs index dfbff7e4da..bc862972e5 100644 --- a/src/ImageSharp/Processing/FlipExtensions.cs +++ b/src/ImageSharp/Processing/FlipExtensions.cs @@ -1,25 +1,22 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Transforms; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of flipping operations to the type. + /// Adds extensions that allow the application of flipping operations to the type. /// public static class FlipExtensions { /// /// Flips an image by the given instructions. /// - /// The pixel format. /// The image to rotate, flip, or both. /// The to perform the flip. /// The - public static IImageProcessingContext Flip(this IImageProcessingContext source, FlipMode flipMode) - where TPixel : struct, IPixel - => source.ApplyProcessor(new FlipProcessor(flipMode)); + public static IImageProcessingContext Flip(this IImageProcessingContext source, FlipMode flipMode) + => source.ApplyProcessor(new FlipProcessor(flipMode)); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs index 9775e5ca93..7efb71fa1c 100644 --- a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs @@ -1,5 +1,5 @@ // Copyright (c) Six Labors and contributors. -/// Licensed under the Apache License, Version 2.0. +// Licensed under the Apache License, Version 2.0. using System; using System.Numerics; diff --git a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs index 7633ed4418..713f042653 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs @@ -1,26 +1,20 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; -using System.Collections.Generic; -using System.Linq; using System.Numerics; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.ParallelUtils; + using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { /// - /// Provides the base methods to perform affine transforms on an image. + /// Defines an affine transformation applicable on an . /// - /// The pixel format. - internal class AffineTransformProcessor : TransformProcessorBase - where TPixel : struct, IPixel + public class AffineTransformProcessor : IImageProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The transform matrix. /// The sampler to perform the transform operation. @@ -48,95 +42,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// public Size TargetDimensions { get; } - /// - protected override Image CreateDestination(Image source, Rectangle sourceRectangle) + /// + public virtual IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel { - // We will always be creating the clone even for mutate because we may need to resize the canvas - IEnumerable> frames = - source.Frames.Select(x => new ImageFrame(source.GetConfiguration(), this.TargetDimensions, x.Metadata.DeepClone())); - - // Use the overload to prevent an extra frame being added - return new Image(source.GetConfiguration(), source.Metadata.DeepClone(), frames); - } - - /// - protected override void OnFrameApply( - ImageFrame source, - ImageFrame destination, - Rectangle sourceRectangle, - Configuration configuration) - { - // Handle tranforms that result in output identical to the original. - if (this.TransformMatrix.Equals(default) || this.TransformMatrix.Equals(Matrix3x2.Identity)) - { - // The clone will be blank here copy all the pixel data over - source.GetPixelSpan().CopyTo(destination.GetPixelSpan()); - return; - } - - int width = this.TargetDimensions.Width; - var targetBounds = new Rectangle(Point.Empty, this.TargetDimensions); - - // Convert from screen to world space. - Matrix3x2.Invert(this.TransformMatrix, out Matrix3x2 matrix); - - if (this.Sampler is NearestNeighborResampler) - { - ParallelHelper.IterateRows( - targetBounds, - configuration, - rows => - { - for (int y = rows.Min; y < rows.Max; y++) - { - Span destRow = destination.GetPixelRowSpan(y); - - for (int x = 0; x < width; x++) - { - var point = Point.Transform(new Point(x, y), matrix); - if (sourceRectangle.Contains(point.X, point.Y)) - { - destRow[x] = source[point.X, point.Y]; - } - } - } - }); - - return; - } - - var kernel = new TransformKernelMap(configuration, source.Size(), destination.Size(), this.Sampler); - try - { - ParallelHelper.IterateRowsWithTempBuffer( - targetBounds, - configuration, - (rows, vectorBuffer) => - { - Span vectorSpan = vectorBuffer.Span; - for (int y = rows.Min; y < rows.Max; y++) - { - Span targetRowSpan = destination.GetPixelRowSpan(y); - PixelOperations.Instance.ToVector4(configuration, targetRowSpan, vectorSpan); - ref float ySpanRef = ref kernel.GetYStartReference(y); - ref float xSpanRef = ref kernel.GetXStartReference(y); - - for (int x = 0; x < width; x++) - { - // Use the single precision position to calculate correct bounding pixels - // otherwise we get rogue pixels outside of the bounds. - var point = Vector2.Transform(new Vector2(x, y), matrix); - kernel.Convolve(point, x, ref ySpanRef, ref xSpanRef, source.PixelBuffer, vectorSpan); - } - - PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan, targetRowSpan); - } - }); - } - finally - { - kernel.Dispose(); - } + return new AffineTransformProcessor(this); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs new file mode 100644 index 0000000000..e57ce826bd --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs @@ -0,0 +1,127 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Transforms +{ + /// + /// Provides the base methods to perform affine transforms on an image. + /// + /// The pixel format. + internal class AffineTransformProcessor : TransformProcessorBase + where TPixel : struct, IPixel + { + public AffineTransformProcessor(AffineTransformProcessor definition) + { + this.Definition = definition; + } + + private Size TargetDimensions => this.Definition.TargetDimensions; + + private Matrix3x2 TransformMatrix => this.Definition.TransformMatrix; + + protected AffineTransformProcessor Definition { get; } + + /// + protected override Image CreateDestination(Image source, Rectangle sourceRectangle) + { + // We will always be creating the clone even for mutate because we may need to resize the canvas + IEnumerable> frames = + source.Frames.Select(x => new ImageFrame(source.GetConfiguration(), this.TargetDimensions, x.Metadata.DeepClone())); + + // Use the overload to prevent an extra frame being added + return new Image(source.GetConfiguration(), source.Metadata.DeepClone(), frames); + } + + /// + protected override void OnFrameApply( + ImageFrame source, + ImageFrame destination, + Rectangle sourceRectangle, + Configuration configuration) + { + // Handle tranforms that result in output identical to the original. + if (this.TransformMatrix.Equals(default) || this.TransformMatrix.Equals(Matrix3x2.Identity)) + { + // The clone will be blank here copy all the pixel data over + source.GetPixelSpan().CopyTo(destination.GetPixelSpan()); + return; + } + + int width = this.TargetDimensions.Width; + var targetBounds = new Rectangle(Point.Empty, this.TargetDimensions); + + // Convert from screen to world space. + Matrix3x2.Invert(this.TransformMatrix, out Matrix3x2 matrix); + + var sampler = this.Definition.Sampler; + + if (sampler is NearestNeighborResampler) + { + ParallelHelper.IterateRows( + targetBounds, + configuration, + rows => + { + for (int y = rows.Min; y < rows.Max; y++) + { + Span destRow = destination.GetPixelRowSpan(y); + + for (int x = 0; x < width; x++) + { + var point = Point.Transform(new Point(x, y), matrix); + if (sourceRectangle.Contains(point.X, point.Y)) + { + destRow[x] = source[point.X, point.Y]; + } + } + } + }); + + return; + } + + var kernel = new TransformKernelMap(configuration, source.Size(), destination.Size(), sampler); + try + { + ParallelHelper.IterateRowsWithTempBuffer( + targetBounds, + configuration, + (rows, vectorBuffer) => + { + Span vectorSpan = vectorBuffer.Span; + for (int y = rows.Min; y < rows.Max; y++) + { + Span targetRowSpan = destination.GetPixelRowSpan(y); + PixelOperations.Instance.ToVector4(configuration, targetRowSpan, vectorSpan); + ref float ySpanRef = ref kernel.GetYStartReference(y); + ref float xSpanRef = ref kernel.GetXStartReference(y); + + for (int x = 0; x < width; x++) + { + // Use the single precision position to calculate correct bounding pixels + // otherwise we get rogue pixels outside of the bounds. + var point = Vector2.Transform(new Vector2(x, y), matrix); + kernel.Convolve(point, x, ref ySpanRef, ref xSpanRef, source.PixelBuffer, vectorSpan); + } + + PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan, targetRowSpan); + } + }); + } + finally + { + kernel.Dispose(); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs index 5b9e3dde23..9bbbba843c 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs @@ -1,103 +1,21 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; -using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { /// /// Adjusts an image so that its orientation is suitable for viewing. Adjustments are based on EXIF metadata embedded in the image. /// - /// The pixel format. - internal class AutoOrientProcessor : ImageProcessor - where TPixel : struct, IPixel + public sealed class AutoOrientProcessor : IImageProcessor { - /// - protected override void BeforeImageApply(Image source, Rectangle sourceRectangle) + /// + public IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel { - OrientationMode orientation = GetExifOrientation(source); - Size size = sourceRectangle.Size; - switch (orientation) - { - case OrientationMode.TopRight: - new FlipProcessor(FlipMode.Horizontal).Apply(source, sourceRectangle); - break; - - case OrientationMode.BottomRight: - new RotateProcessor((int)RotateMode.Rotate180, size).Apply(source, sourceRectangle); - break; - - case OrientationMode.BottomLeft: - new FlipProcessor(FlipMode.Vertical).Apply(source, sourceRectangle); - break; - - case OrientationMode.LeftTop: - new RotateProcessor((int)RotateMode.Rotate90, size).Apply(source, sourceRectangle); - new FlipProcessor(FlipMode.Horizontal).Apply(source, sourceRectangle); - break; - - case OrientationMode.RightTop: - new RotateProcessor((int)RotateMode.Rotate90, size).Apply(source, sourceRectangle); - break; - - case OrientationMode.RightBottom: - new FlipProcessor(FlipMode.Vertical).Apply(source, sourceRectangle); - new RotateProcessor((int)RotateMode.Rotate270, size).Apply(source, sourceRectangle); - break; - - case OrientationMode.LeftBottom: - new RotateProcessor((int)RotateMode.Rotate270, size).Apply(source, sourceRectangle); - break; - - case OrientationMode.Unknown: - case OrientationMode.TopLeft: - default: - break; - } - } - - /// - protected override void OnFrameApply(ImageFrame sourceBase, Rectangle sourceRectangle, Configuration config) - { - // All processing happens at the image level within BeforeImageApply(); - } - - /// - /// Returns the current EXIF orientation - /// - /// The image to auto rotate. - /// The - private static OrientationMode GetExifOrientation(Image source) - { - if (source.Metadata.ExifProfile is null) - { - return OrientationMode.Unknown; - } - - ExifValue value = source.Metadata.ExifProfile.GetValue(ExifTag.Orientation); - if (value is null) - { - return OrientationMode.Unknown; - } - - OrientationMode orientation; - if (value.DataType == ExifDataType.Short) - { - orientation = (OrientationMode)value.Value; - } - else - { - orientation = (OrientationMode)Convert.ToUInt16(value.Value); - source.Metadata.ExifProfile.RemoveValue(ExifTag.Orientation); - } - - source.Metadata.ExifProfile.SetValue(ExifTag.Orientation, (ushort)OrientationMode.TopLeft); - - return orientation; + return new AutoOrientProcessor(); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs new file mode 100644 index 0000000000..257c223dc4 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs @@ -0,0 +1,103 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +using SixLabors.ImageSharp.Metadata.Profiles.Exif; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Transforms +{ + /// + /// Adjusts an image so that its orientation is suitable for viewing. Adjustments are based on EXIF metadata embedded in the image. + /// + /// The pixel format. + internal class AutoOrientProcessor : ImageProcessor + where TPixel : struct, IPixel + { + /// + protected override void BeforeImageApply(Image source, Rectangle sourceRectangle) + { + OrientationMode orientation = GetExifOrientation(source); + Size size = sourceRectangle.Size; + switch (orientation) + { + case OrientationMode.TopRight: + new FlipProcessor(FlipMode.Horizontal).Apply(source, sourceRectangle); + break; + + case OrientationMode.BottomRight: + new RotateProcessor((int)RotateMode.Rotate180, size).Apply(source, sourceRectangle); + break; + + case OrientationMode.BottomLeft: + new FlipProcessor(FlipMode.Vertical).Apply(source, sourceRectangle); + break; + + case OrientationMode.LeftTop: + new RotateProcessor((int)RotateMode.Rotate90, size).Apply(source, sourceRectangle); + new FlipProcessor(FlipMode.Horizontal).Apply(source, sourceRectangle); + break; + + case OrientationMode.RightTop: + new RotateProcessor((int)RotateMode.Rotate90, size).Apply(source, sourceRectangle); + break; + + case OrientationMode.RightBottom: + new FlipProcessor(FlipMode.Vertical).Apply(source, sourceRectangle); + new RotateProcessor((int)RotateMode.Rotate270, size).Apply(source, sourceRectangle); + break; + + case OrientationMode.LeftBottom: + new RotateProcessor((int)RotateMode.Rotate270, size).Apply(source, sourceRectangle); + break; + + case OrientationMode.Unknown: + case OrientationMode.TopLeft: + default: + break; + } + } + + /// + protected override void OnFrameApply(ImageFrame sourceBase, Rectangle sourceRectangle, Configuration config) + { + // All processing happens at the image level within BeforeImageApply(); + } + + /// + /// Returns the current EXIF orientation + /// + /// The image to auto rotate. + /// The + private static OrientationMode GetExifOrientation(Image source) + { + if (source.Metadata.ExifProfile is null) + { + return OrientationMode.Unknown; + } + + ExifValue value = source.Metadata.ExifProfile.GetValue(ExifTag.Orientation); + if (value is null) + { + return OrientationMode.Unknown; + } + + OrientationMode orientation; + if (value.DataType == ExifDataType.Short) + { + orientation = (OrientationMode)value.Value; + } + else + { + orientation = (OrientationMode)Convert.ToUInt16(value.Value); + source.Metadata.ExifProfile.RemoveValue(ExifTag.Orientation); + } + + source.Metadata.ExifProfile.SetValue(ExifTag.Orientation, (ushort)OrientationMode.TopLeft); + + return orientation; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs index 5baa196a09..76f223e038 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs @@ -1,32 +1,28 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; -using System.Collections.Generic; -using System.Linq; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { /// - /// Provides methods to allow the cropping of an image. + /// Defines a crop operation on an image. /// - /// The pixel format. - internal class CropProcessor : TransformProcessorBase - where TPixel : struct, IPixel + public sealed class CropProcessor : IImageProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The target cropped rectangle. /// The source image size. public CropProcessor(Rectangle cropRectangle, Size sourceSize) { // Check bounds here and throw if we are passed a rectangle exceeding our source bounds. - Guard.IsTrue(new Rectangle(Point.Empty, sourceSize).Contains(cropRectangle), nameof(cropRectangle), "Crop rectangle should be smaller than the source bounds."); + Guard.IsTrue( + new Rectangle(Point.Empty, sourceSize).Contains(cropRectangle), + nameof(cropRectangle), + "Crop rectangle should be smaller than the source bounds."); this.CropRectangle = cropRectangle; } @@ -35,44 +31,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// public Rectangle CropRectangle { get; } - /// - protected override Image CreateDestination(Image source, Rectangle sourceRectangle) + /// + public IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel { - // We will always be creating the clone even for mutate because we may need to resize the canvas - IEnumerable> frames = source.Frames.Select(x => new ImageFrame(source.GetConfiguration(), this.CropRectangle.Width, this.CropRectangle.Height, x.Metadata.DeepClone())); - - // Use the overload to prevent an extra frame being added - return new Image(source.GetConfiguration(), source.Metadata.DeepClone(), frames); - } - - /// - protected override void OnFrameApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) - { - // Handle resize dimensions identical to the original - if (source.Width == destination.Width && source.Height == destination.Height && sourceRectangle == this.CropRectangle) - { - // the cloned will be blank here copy all the pixel data over - source.GetPixelSpan().CopyTo(destination.GetPixelSpan()); - return; - } - - Rectangle rect = this.CropRectangle; - - // Copying is cheap, we should process more pixels per task: - ParallelExecutionSettings parallelSettings = configuration.GetParallelSettings().MultiplyMinimumPixelsPerTask(4); - - ParallelHelper.IterateRows( - rect, - parallelSettings, - rows => - { - for (int y = rows.Min; y < rows.Max; y++) - { - Span sourceRow = source.GetPixelRowSpan(y).Slice(rect.Left); - Span targetRow = destination.GetPixelRowSpan(y - rect.Top); - sourceRow.Slice(0, rect.Width).CopyTo(targetRow); - } - }); + return new CropProcessor(this); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs new file mode 100644 index 0000000000..9bddda3825 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs @@ -0,0 +1,75 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Linq; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Transforms +{ + /// + /// Provides methods to allow the cropping of an image. + /// + /// The pixel format. + internal class CropProcessor : TransformProcessorBase + where TPixel : struct, IPixel + { + private readonly CropProcessor definition; + + /// + /// Initializes a new instance of the class. + /// + /// The . + public CropProcessor(CropProcessor definition) + { + this.definition = definition; + } + + private Rectangle CropRectangle => this.definition.CropRectangle; + + /// + protected override Image CreateDestination(Image source, Rectangle sourceRectangle) + { + // We will always be creating the clone even for mutate because we may need to resize the canvas + IEnumerable> frames = source.Frames.Select(x => new ImageFrame(source.GetConfiguration(), this.CropRectangle.Width, this.CropRectangle.Height, x.Metadata.DeepClone())); + + // Use the overload to prevent an extra frame being added + return new Image(source.GetConfiguration(), source.Metadata.DeepClone(), frames); + } + + /// + protected override void OnFrameApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) + { + // Handle resize dimensions identical to the original + if (source.Width == destination.Width && source.Height == destination.Height && sourceRectangle == this.CropRectangle) + { + // the cloned will be blank here copy all the pixel data over + source.GetPixelSpan().CopyTo(destination.GetPixelSpan()); + return; + } + + Rectangle rect = this.CropRectangle; + + // Copying is cheap, we should process more pixels per task: + ParallelExecutionSettings parallelSettings = configuration.GetParallelSettings().MultiplyMinimumPixelsPerTask(4); + + ParallelHelper.IterateRows( + rect, + parallelSettings, + rows => + { + for (int y = rows.Min; y < rows.Max; y++) + { + Span sourceRow = source.GetPixelRowSpan(y).Slice(rect.Left); + Span targetRow = destination.GetPixelRowSpan(y - rect.Top); + sourceRow.Slice(0, rect.Width).CopyTo(targetRow); + } + }); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs index 0e744dd963..dee5e7fb37 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs @@ -1,31 +1,25 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing.Processors.Binarization; -using SixLabors.ImageSharp.Processing.Processors.Convolution; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { /// - /// Provides methods to allow the cropping of an image to preserve areas of highest entropy. + /// Defines cropping operation that preserves areas of highest entropy. /// - /// The pixel format. - internal class EntropyCropProcessor : ImageProcessor - where TPixel : struct, IPixel + public sealed class EntropyCropProcessor : IImageProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public EntropyCropProcessor() - : this(.5F) + : this(.5F) { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The threshold to split the image. Must be between 0 and 1. /// @@ -42,33 +36,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// public float Threshold { get; } - /// - protected override void BeforeImageApply(Image source, Rectangle sourceRectangle) + /// + public IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel { - Rectangle rectangle; - - // All frames have be the same size so we only need to calculate the correct dimensions for the first frame - using (ImageFrame temp = source.Frames.RootFrame.Clone()) - { - Configuration configuration = source.GetConfiguration(); - - // Detect the edges. - new SobelProcessor(false).ApplyToFrame(temp, sourceRectangle, configuration); - - // Apply threshold binarization filter. - new BinaryThresholdProcessor(this.Threshold).Apply(temp, sourceRectangle, configuration); - - // Search for the first white pixels - rectangle = ImageMaths.GetFilteredBoundingRectangle(temp, 0); - } - - new CropProcessor(rectangle, source.Size()).Apply(source, sourceRectangle); - } - - /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - { - // All processing happens at the image level within BeforeImageApply(); + return new EntropyCropProcessor(this); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs new file mode 100644 index 0000000000..eaeb6939ec --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs @@ -0,0 +1,59 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors.Binarization; +using SixLabors.ImageSharp.Processing.Processors.Convolution; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Transforms +{ + /// + /// Provides methods to allow the cropping of an image to preserve areas of highest entropy. + /// + /// The pixel format. + internal class EntropyCropProcessor : ImageProcessor + where TPixel : struct, IPixel + { + private readonly EntropyCropProcessor definition; + + /// + /// Initializes a new instance of the class. + /// + /// The . + public EntropyCropProcessor(EntropyCropProcessor definition) + { + this.definition = definition; + } + + /// + protected override void BeforeImageApply(Image source, Rectangle sourceRectangle) + { + Rectangle rectangle; + + // All frames have be the same size so we only need to calculate the correct dimensions for the first frame + using (ImageFrame temp = source.Frames.RootFrame.Clone()) + { + Configuration configuration = source.GetConfiguration(); + + // Detect the edges. + new SobelProcessor(false).ApplyToFrame(temp, sourceRectangle, configuration); + + // Apply threshold binarization filter. + new BinaryThresholdProcessor(this.definition.Threshold).Apply(temp, sourceRectangle, configuration); + + // Search for the first white pixels + rectangle = ImageMaths.GetFilteredBoundingRectangle(temp, 0); + } + + new CropProcessor(rectangle, source.Size()).Apply(source, sourceRectangle); + } + + /// + protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + // All processing happens at the image level within BeforeImageApply(); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs index c6f5e9d7b8..9a3eab6cc8 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs @@ -1,27 +1,17 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; -using System.Buffers; -using System.Threading.Tasks; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { /// - /// Provides methods that allow the flipping of an image around its center point. + /// Defines a flipping around the center point of the image. /// - /// The pixel format. - internal class FlipProcessor : ImageProcessor - where TPixel : struct, IPixel + public class FlipProcessor : IImageProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The used to perform flipping. public FlipProcessor(FlipMode flipMode) @@ -34,63 +24,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// public FlipMode FlipMode { get; } - /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + /// + public IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel { - switch (this.FlipMode) - { - // No default needed as we have already set the pixels. - case FlipMode.Vertical: - this.FlipX(source, configuration); - break; - case FlipMode.Horizontal: - this.FlipY(source, configuration); - break; - } - } - - /// - /// Swaps the image at the X-axis, which goes horizontally through the middle at half the height of the image. - /// - /// The source image to apply the process to. - /// The configuration. - private void FlipX(ImageFrame source, Configuration configuration) - { - int height = source.Height; - - using (IMemoryOwner tempBuffer = configuration.MemoryAllocator.Allocate(source.Width)) - { - Span temp = tempBuffer.Memory.Span; - - for (int yTop = 0; yTop < height / 2; yTop++) - { - int yBottom = height - yTop - 1; - Span topRow = source.GetPixelRowSpan(yBottom); - Span bottomRow = source.GetPixelRowSpan(yTop); - topRow.CopyTo(temp); - bottomRow.CopyTo(topRow); - temp.CopyTo(bottomRow); - } - } - } - - /// - /// Swaps the image at the Y-axis, which goes vertically through the middle at half of the width of the image. - /// - /// The source image to apply the process to. - /// The configuration. - private void FlipY(ImageFrame source, Configuration configuration) - { - ParallelHelper.IterateRows( - source.Bounds(), - configuration, - rows => - { - for (int y = rows.Min; y < rows.Max; y++) - { - source.GetPixelRowSpan(y).Reverse(); - } - }); + return new FlipProcessor(this); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor{TPixel}.cs new file mode 100644 index 0000000000..0247862096 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor{TPixel}.cs @@ -0,0 +1,87 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Transforms +{ + /// + /// Provides methods that allow the flipping of an image around its center point. + /// + /// The pixel format. + internal class FlipProcessor : ImageProcessor + where TPixel : struct, IPixel + { + private readonly FlipProcessor definition; + + public FlipProcessor(FlipProcessor definition) + { + this.definition = definition; + } + + /// + protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + switch (this.definition.FlipMode) + { + // No default needed as we have already set the pixels. + case FlipMode.Vertical: + this.FlipX(source, configuration); + break; + case FlipMode.Horizontal: + this.FlipY(source, configuration); + break; + } + } + + /// + /// Swaps the image at the X-axis, which goes horizontally through the middle at half the height of the image. + /// + /// The source image to apply the process to. + /// The configuration. + private void FlipX(ImageFrame source, Configuration configuration) + { + int height = source.Height; + + using (IMemoryOwner tempBuffer = configuration.MemoryAllocator.Allocate(source.Width)) + { + Span temp = tempBuffer.Memory.Span; + + for (int yTop = 0; yTop < height / 2; yTop++) + { + int yBottom = height - yTop - 1; + Span topRow = source.GetPixelRowSpan(yBottom); + Span bottomRow = source.GetPixelRowSpan(yTop); + topRow.CopyTo(temp); + bottomRow.CopyTo(topRow); + temp.CopyTo(bottomRow); + } + } + } + + /// + /// Swaps the image at the Y-axis, which goes vertically through the middle at half of the width of the image. + /// + /// The source image to apply the process to. + /// The configuration. + private void FlipY(ImageFrame source, Configuration configuration) + { + ParallelHelper.IterateRows( + source.Bounds(), + configuration, + rows => + { + for (int y = rows.Min; y < rows.Max; y++) + { + source.GetPixelRowSpan(y).Reverse(); + } + }); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs index 6c7271c5ec..3a86b3fe4f 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs @@ -1,26 +1,20 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; -using System.Collections.Generic; -using System.Linq; using System.Numerics; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.ParallelUtils; + using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { /// - /// Provides the base methods to perform non-affine transforms on an image. + /// Defines a projective transformation applicable to an . /// - /// The pixel format. - internal class ProjectiveTransformProcessor : TransformProcessorBase - where TPixel : struct, IPixel + public sealed class ProjectiveTransformProcessor : IImageProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The transform matrix. /// The sampler to perform the transform operation. @@ -39,103 +33,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public IResampler Sampler { get; } /// - /// Gets the matrix used to supply the projective transform + /// Gets the matrix used to supply the projective transform. /// public Matrix4x4 TransformMatrix { get; } /// - /// Gets the target dimensions to constrain the transformed image to + /// Gets the target dimensions to constrain the transformed image to. /// public Size TargetDimensions { get; } - /// - protected override Image CreateDestination(Image source, Rectangle sourceRectangle) + /// + public IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel { - // We will always be creating the clone even for mutate because we may need to resize the canvas - IEnumerable> frames = - source.Frames.Select(x => new ImageFrame(source.GetConfiguration(), this.TargetDimensions.Width, this.TargetDimensions.Height, x.Metadata.DeepClone())); - - // Use the overload to prevent an extra frame being added - return new Image(source.GetConfiguration(), source.Metadata.DeepClone(), frames); - } - - /// - protected override void OnFrameApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) - { - // Handle tranforms that result in output identical to the original. - if (this.TransformMatrix.Equals(default) || this.TransformMatrix.Equals(Matrix4x4.Identity)) - { - // The clone will be blank here copy all the pixel data over - source.GetPixelSpan().CopyTo(destination.GetPixelSpan()); - return; - } - - int width = this.TargetDimensions.Width; - var targetBounds = new Rectangle(Point.Empty, this.TargetDimensions); - - // Convert from screen to world space. - Matrix4x4.Invert(this.TransformMatrix, out Matrix4x4 matrix); - - if (this.Sampler is NearestNeighborResampler) - { - ParallelHelper.IterateRows( - targetBounds, - configuration, - rows => - { - for (int y = rows.Min; y < rows.Max; y++) - { - Span destRow = destination.GetPixelRowSpan(y); - - for (int x = 0; x < width; x++) - { - Vector2 point = TransformUtils.ProjectiveTransform2D(x, y, matrix); - int px = (int)MathF.Round(point.X); - int py = (int)MathF.Round(point.Y); - - if (sourceRectangle.Contains(px, py)) - { - destRow[x] = source[px, py]; - } - } - } - }); - - return; - } - - var kernel = new TransformKernelMap(configuration, source.Size(), destination.Size(), this.Sampler); - try - { - ParallelHelper.IterateRowsWithTempBuffer( - targetBounds, - configuration, - (rows, vectorBuffer) => - { - Span vectorSpan = vectorBuffer.Span; - for (int y = rows.Min; y < rows.Max; y++) - { - Span targetRowSpan = destination.GetPixelRowSpan(y); - PixelOperations.Instance.ToVector4(configuration, targetRowSpan, vectorSpan); - ref float ySpanRef = ref kernel.GetYStartReference(y); - ref float xSpanRef = ref kernel.GetXStartReference(y); - - for (int x = 0; x < width; x++) - { - // Use the single precision position to calculate correct bounding pixels - // otherwise we get rogue pixels outside of the bounds. - Vector2 point = TransformUtils.ProjectiveTransform2D(x, y, matrix); - kernel.Convolve(point, x, ref ySpanRef, ref xSpanRef, source.PixelBuffer, vectorSpan); - } - - PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan, targetRowSpan); - } - }); - } - finally - { - kernel.Dispose(); - } + return new ProjectiveTransformProcessor(this); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs new file mode 100644 index 0000000000..e6d885803b --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs @@ -0,0 +1,126 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Transforms +{ + /// + /// Provides the base methods to perform non-affine transforms on an image. + /// + /// The pixel format. + internal class ProjectiveTransformProcessor : TransformProcessorBase + where TPixel : struct, IPixel + { + private readonly ProjectiveTransformProcessor definition; + + public ProjectiveTransformProcessor(ProjectiveTransformProcessor definition) + { + this.definition = definition; + } + + private Size TargetDimensions => this.definition.TargetDimensions; + + /// + protected override Image CreateDestination(Image source, Rectangle sourceRectangle) + { + // We will always be creating the clone even for mutate because we may need to resize the canvas + IEnumerable> frames = + source.Frames.Select(x => new ImageFrame(source.GetConfiguration(), this.TargetDimensions.Width, this.TargetDimensions.Height, x.Metadata.DeepClone())); + + // Use the overload to prevent an extra frame being added + return new Image(source.GetConfiguration(), source.Metadata.DeepClone(), frames); + } + + /// + protected override void OnFrameApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) + { + Matrix4x4 transformMatrix = this.definition.TransformMatrix; + + // Handle tranforms that result in output identical to the original. + if (transformMatrix.Equals(default) || transformMatrix.Equals(Matrix4x4.Identity)) + { + // The clone will be blank here copy all the pixel data over + source.GetPixelSpan().CopyTo(destination.GetPixelSpan()); + return; + } + + int width = this.TargetDimensions.Width; + var targetBounds = new Rectangle(Point.Empty, this.TargetDimensions); + + // Convert from screen to world space. + Matrix4x4.Invert(transformMatrix, out Matrix4x4 matrix); + + IResampler sampler = this.definition.Sampler; + + if (sampler is NearestNeighborResampler) + { + ParallelHelper.IterateRows( + targetBounds, + configuration, + rows => + { + for (int y = rows.Min; y < rows.Max; y++) + { + Span destRow = destination.GetPixelRowSpan(y); + + for (int x = 0; x < width; x++) + { + Vector2 point = TransformUtils.ProjectiveTransform2D(x, y, matrix); + int px = (int)MathF.Round(point.X); + int py = (int)MathF.Round(point.Y); + + if (sourceRectangle.Contains(px, py)) + { + destRow[x] = source[px, py]; + } + } + } + }); + + return; + } + + var kernel = new TransformKernelMap(configuration, source.Size(), destination.Size(), sampler); + try + { + ParallelHelper.IterateRowsWithTempBuffer( + targetBounds, + configuration, + (rows, vectorBuffer) => + { + Span vectorSpan = vectorBuffer.Span; + for (int y = rows.Min; y < rows.Max; y++) + { + Span targetRowSpan = destination.GetPixelRowSpan(y); + PixelOperations.Instance.ToVector4(configuration, targetRowSpan, vectorSpan); + ref float ySpanRef = ref kernel.GetYStartReference(y); + ref float xSpanRef = ref kernel.GetXStartReference(y); + + for (int x = 0; x < width; x++) + { + // Use the single precision position to calculate correct bounding pixels + // otherwise we get rogue pixels outside of the bounds. + Vector2 point = TransformUtils.ProjectiveTransform2D(x, y, matrix); + kernel.Convolve(point, x, ref ySpanRef, ref xSpanRef, source.PixelBuffer, vectorSpan); + } + + PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan, targetRowSpan); + } + }); + } + finally + { + kernel.Dispose(); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs index 57902a5e6d..ef0671d20d 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs @@ -1,26 +1,19 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; using System.Numerics; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Metadata.Profiles.Exif; -using SixLabors.ImageSharp.ParallelUtils; -using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { /// - /// Provides methods that allow the rotating of images. + /// Defines a rotation applicable to an . /// - /// The pixel format. - internal class RotateProcessor : AffineTransformProcessor - where TPixel : struct, IPixel + public sealed class RotateProcessor : AffineTransformProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The angle of rotation in degrees. /// The source image size @@ -30,16 +23,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The angle of rotation in degrees. /// The sampler to perform the rotating operation. /// The source image size public RotateProcessor(float degrees, IResampler sampler, Size sourceSize) : this( - TransformUtils.CreateRotationMatrixDegrees(degrees, sourceSize), - sampler, - sourceSize) + TransformUtils.CreateRotationMatrixDegrees(degrees, sourceSize), + sampler, + sourceSize) => this.Degrees = degrees; // Helper constructor @@ -53,190 +46,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// public float Degrees { get; } - /// - protected override void OnFrameApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) + /// + public override IImageProcessor CreatePixelSpecificProcessor() { - if (this.OptimizedApply(source, destination, configuration)) - { - return; - } - - base.OnFrameApply(source, destination, sourceRectangle, configuration); - } - - /// - protected override void AfterImageApply(Image source, Image destination, Rectangle sourceRectangle) - { - ExifProfile profile = destination.Metadata.ExifProfile; - if (profile is null) - { - return; - } - - if (MathF.Abs(WrapDegrees(this.Degrees)) < Constants.Epsilon) - { - // No need to do anything so return. - return; - } - - profile.RemoveValue(ExifTag.Orientation); - - base.AfterImageApply(source, destination, sourceRectangle); - } - - /// - /// Wraps a given angle in degrees so that it falls withing the 0-360 degree range - /// - /// The angle of rotation in degrees. - /// The - private static float WrapDegrees(float degrees) - { - degrees %= 360; - - while (degrees < 0) - { - degrees += 360; - } - - return degrees; - } - - /// - /// Rotates the images with an optimized method when the angle is 90, 180 or 270 degrees. - /// - /// The source image. - /// The destination image. - /// The configuration. - /// - /// The - /// - private bool OptimizedApply(ImageFrame source, ImageFrame destination, Configuration configuration) - { - // Wrap the degrees to keep within 0-360 so we can apply optimizations when possible. - float degrees = WrapDegrees(this.Degrees); - - if (MathF.Abs(degrees) < Constants.Epsilon) - { - // The destination will be blank here so copy all the pixel data over - source.GetPixelSpan().CopyTo(destination.GetPixelSpan()); - return true; - } - - if (MathF.Abs(degrees - 90) < Constants.Epsilon) - { - this.Rotate90(source, destination, configuration); - return true; - } - - if (MathF.Abs(degrees - 180) < Constants.Epsilon) - { - this.Rotate180(source, destination, configuration); - return true; - } - - if (MathF.Abs(degrees - 270) < Constants.Epsilon) - { - this.Rotate270(source, destination, configuration); - return true; - } - - return false; - } - - /// - /// Rotates the image 270 degrees clockwise at the centre point. - /// - /// The source image. - /// The destination image. - /// The configuration. - private void Rotate270(ImageFrame source, ImageFrame destination, Configuration configuration) - { - int width = source.Width; - int height = source.Height; - Rectangle destinationBounds = destination.Bounds(); - - ParallelHelper.IterateRows( - source.Bounds(), - configuration, - rows => - { - for (int y = rows.Min; y < rows.Max; y++) - { - Span sourceRow = source.GetPixelRowSpan(y); - for (int x = 0; x < width; x++) - { - int newX = height - y - 1; - newX = height - newX - 1; - int newY = width - x - 1; - - if (destinationBounds.Contains(newX, newY)) - { - destination[newX, newY] = sourceRow[x]; - } - } - } - }); - } - - /// - /// Rotates the image 180 degrees clockwise at the centre point. - /// - /// The source image. - /// The destination image. - /// The configuration. - private void Rotate180(ImageFrame source, ImageFrame destination, Configuration configuration) - { - int width = source.Width; - int height = source.Height; - - ParallelHelper.IterateRows( - source.Bounds(), - configuration, - rows => - { - for (int y = rows.Min; y < rows.Max; y++) - { - Span sourceRow = source.GetPixelRowSpan(y); - Span targetRow = destination.GetPixelRowSpan(height - y - 1); - - for (int x = 0; x < width; x++) - { - targetRow[width - x - 1] = sourceRow[x]; - } - } - }); - } - - /// - /// Rotates the image 90 degrees clockwise at the centre point. - /// - /// The source image. - /// The destination image. - /// The configuration. - private void Rotate90(ImageFrame source, ImageFrame destination, Configuration configuration) - { - int width = source.Width; - int height = source.Height; - Rectangle destinationBounds = destination.Bounds(); - - ParallelHelper.IterateRows( - source.Bounds(), - configuration, - rows => - { - for (int y = rows.Min; y < rows.Max; y++) - { - Span sourceRow = source.GetPixelRowSpan(y); - int newX = height - y - 1; - for (int x = 0; x < width; x++) - { - if (destinationBounds.Contains(newX, x)) - { - destination[newX, x] = sourceRow[x]; - } - } - } - }); + return new RotateProcessor(this); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs new file mode 100644 index 0000000000..aac6f65148 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs @@ -0,0 +1,215 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Metadata.Profiles.Exif; +using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Transforms +{ + /// + /// Provides methods that allow the rotating of images. + /// + /// The pixel format. + internal class RotateProcessor : AffineTransformProcessor + where TPixel : struct, IPixel + { + public RotateProcessor(RotateProcessor definition) + : base(definition) + { + this.Degrees = definition.Degrees; + } + + private float Degrees { get; } + + /// + protected override void OnFrameApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) + { + if (this.OptimizedApply(source, destination, configuration)) + { + return; + } + + base.OnFrameApply(source, destination, sourceRectangle, configuration); + } + + /// + protected override void AfterImageApply(Image source, Image destination, Rectangle sourceRectangle) + { + ExifProfile profile = destination.Metadata.ExifProfile; + if (profile is null) + { + return; + } + + if (MathF.Abs(WrapDegrees(this.Degrees)) < Constants.Epsilon) + { + // No need to do anything so return. + return; + } + + profile.RemoveValue(ExifTag.Orientation); + + base.AfterImageApply(source, destination, sourceRectangle); + } + + /// + /// Wraps a given angle in degrees so that it falls withing the 0-360 degree range + /// + /// The angle of rotation in degrees. + /// The . + private static float WrapDegrees(float degrees) + { + degrees %= 360; + + while (degrees < 0) + { + degrees += 360; + } + + return degrees; + } + + /// + /// Rotates the images with an optimized method when the angle is 90, 180 or 270 degrees. + /// + /// The source image. + /// The destination image. + /// The configuration. + /// + /// The + /// + private bool OptimizedApply(ImageFrame source, ImageFrame destination, Configuration configuration) + { + // Wrap the degrees to keep within 0-360 so we can apply optimizations when possible. + float degrees = WrapDegrees(this.Degrees); + + if (MathF.Abs(degrees) < Constants.Epsilon) + { + // The destination will be blank here so copy all the pixel data over + source.GetPixelSpan().CopyTo(destination.GetPixelSpan()); + return true; + } + + if (MathF.Abs(degrees - 90) < Constants.Epsilon) + { + this.Rotate90(source, destination, configuration); + return true; + } + + if (MathF.Abs(degrees - 180) < Constants.Epsilon) + { + this.Rotate180(source, destination, configuration); + return true; + } + + if (MathF.Abs(degrees - 270) < Constants.Epsilon) + { + this.Rotate270(source, destination, configuration); + return true; + } + + return false; + } + + /// + /// Rotates the image 270 degrees clockwise at the centre point. + /// + /// The source image. + /// The destination image. + /// The configuration. + private void Rotate270(ImageFrame source, ImageFrame destination, Configuration configuration) + { + int width = source.Width; + int height = source.Height; + Rectangle destinationBounds = destination.Bounds(); + + ParallelHelper.IterateRows( + source.Bounds(), + configuration, + rows => + { + for (int y = rows.Min; y < rows.Max; y++) + { + Span sourceRow = source.GetPixelRowSpan(y); + for (int x = 0; x < width; x++) + { + int newX = height - y - 1; + newX = height - newX - 1; + int newY = width - x - 1; + + if (destinationBounds.Contains(newX, newY)) + { + destination[newX, newY] = sourceRow[x]; + } + } + } + }); + } + + /// + /// Rotates the image 180 degrees clockwise at the centre point. + /// + /// The source image. + /// The destination image. + /// The configuration. + private void Rotate180(ImageFrame source, ImageFrame destination, Configuration configuration) + { + int width = source.Width; + int height = source.Height; + + ParallelHelper.IterateRows( + source.Bounds(), + configuration, + rows => + { + for (int y = rows.Min; y < rows.Max; y++) + { + Span sourceRow = source.GetPixelRowSpan(y); + Span targetRow = destination.GetPixelRowSpan(height - y - 1); + + for (int x = 0; x < width; x++) + { + targetRow[width - x - 1] = sourceRow[x]; + } + } + }); + } + + /// + /// Rotates the image 90 degrees clockwise at the centre point. + /// + /// The source image. + /// The destination image. + /// The configuration. + private void Rotate90(ImageFrame source, ImageFrame destination, Configuration configuration) + { + int width = source.Width; + int height = source.Height; + Rectangle destinationBounds = destination.Bounds(); + + ParallelHelper.IterateRows( + source.Bounds(), + configuration, + rows => + { + for (int y = rows.Min; y < rows.Max; y++) + { + Span sourceRow = source.GetPixelRowSpan(y); + int newX = height - y - 1; + for (int x = 0; x < width; x++) + { + if (destinationBounds.Contains(newX, x)) + { + destination[newX, x] = sourceRow[x]; + } + } + } + }); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs index c7b1d74104..35c3717eea 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs @@ -9,14 +9,12 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { /// - /// Provides methods that allow the skewing of images. + /// Defines a skew transformation applicable to an . /// - /// The pixel format. - internal class SkewProcessor : AffineTransformProcessor - where TPixel : struct, IPixel + internal class SkewProcessor : AffineTransformProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The angle in degrees to perform the skew along the x-axis. /// The angle in degrees to perform the skew along the y-axis. @@ -27,7 +25,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The angle in degrees to perform the skew along the x-axis. /// The angle in degrees to perform the skew along the y-axis. diff --git a/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorBase.cs b/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorBase.cs index 4973b90f46..286ada2e57 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorBase.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorBase.cs @@ -16,6 +16,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms { /// protected override void AfterImageApply(Image source, Image destination, Rectangle sourceRectangle) - => TransformProcessorHelpers.UpdateDimensionalMetData(destination); + => TransformProcessorHelpers.UpdateDimensionalMetadata(destination); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorHelpers.cs b/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorHelpers.cs index 7bb666bce5..00c1227a6c 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorHelpers.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorHelpers.cs @@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// /// The pixel format. /// The image to update - public static void UpdateDimensionalMetData(Image image) + public static void UpdateDimensionalMetadata(Image image) where TPixel : struct, IPixel { ExifProfile profile = image.Metadata.ExifProfile; diff --git a/src/ImageSharp/Processing/RotateExtensions.cs b/src/ImageSharp/Processing/RotateExtensions.cs index 398a634d10..cb637a1b84 100644 --- a/src/ImageSharp/Processing/RotateExtensions.cs +++ b/src/ImageSharp/Processing/RotateExtensions.cs @@ -1,48 +1,44 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Transforms; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of rotate operations to the type. + /// Adds extensions that allow the application of rotate operations to the type. /// public static class RotateExtensions { /// /// Rotates and flips an image by the given instructions. /// - /// The pixel format. /// The image to rotate. /// The to perform the rotation. /// The - public static IImageProcessingContext Rotate(this IImageProcessingContext source, RotateMode rotateMode) - where TPixel : struct, IPixel - => Rotate(source, (float)rotateMode); + public static IImageProcessingContext Rotate(this IImageProcessingContext source, RotateMode rotateMode) => + Rotate(source, (float)rotateMode); /// /// Rotates an image by the given angle in degrees. /// - /// The pixel format. /// The image to rotate. /// The angle in degrees to perform the rotation. /// The - public static IImageProcessingContext Rotate(this IImageProcessingContext source, float degrees) - where TPixel : struct, IPixel - => Rotate(source, degrees, KnownResamplers.Bicubic); + public static IImageProcessingContext Rotate(this IImageProcessingContext source, float degrees) => + Rotate(source, degrees, KnownResamplers.Bicubic); /// /// Rotates an image by the given angle in degrees using the specified sampling algorithm. /// - /// The pixel format. /// The image to rotate. /// The angle in degrees to perform the rotation. /// The to perform the resampling. /// The - public static IImageProcessingContext Rotate(this IImageProcessingContext source, float degrees, IResampler sampler) - where TPixel : struct, IPixel - => source.ApplyProcessor(new RotateProcessor(degrees, sampler, source.GetCurrentSize())); + public static IImageProcessingContext Rotate( + this IImageProcessingContext source, + float degrees, + IResampler sampler) => + source.ApplyProcessor(new RotateProcessor(degrees, sampler, source.GetCurrentSize())); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/RotateFlipExtensions.cs b/src/ImageSharp/Processing/RotateFlipExtensions.cs index 27ddc8de96..5030c942ef 100644 --- a/src/ImageSharp/Processing/RotateFlipExtensions.cs +++ b/src/ImageSharp/Processing/RotateFlipExtensions.cs @@ -6,20 +6,18 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of rotate-flip operations to the type. + /// Adds extensions that allow the application of rotate-flip operations to the type. /// public static class RotateFlipExtensions { /// /// Rotates and flips an image by the given instructions. /// - /// The pixel format. /// The image to rotate, flip, or both. /// The to perform the rotation. /// The to perform the flip. /// The - public static IImageProcessingContext RotateFlip(this IImageProcessingContext source, RotateMode rotateMode, FlipMode flipMode) - where TPixel : struct, IPixel + public static IImageProcessingContext RotateFlip(this IImageProcessingContext source, RotateMode rotateMode, FlipMode flipMode) => source.Rotate(rotateMode).Flip(flipMode); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/SkewExtensions.cs b/src/ImageSharp/Processing/SkewExtensions.cs index 07e3c6087d..7f378d2483 100644 --- a/src/ImageSharp/Processing/SkewExtensions.cs +++ b/src/ImageSharp/Processing/SkewExtensions.cs @@ -1,39 +1,39 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Transforms; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of skew operations to the type. + /// Adds extensions that allow the application of skew operations to the type. /// public static class SkewExtensions { /// /// Skews an image by the given angles in degrees. /// - /// The pixel format. /// The image to skew. /// The angle in degrees to perform the skew along the x-axis. /// The angle in degrees to perform the skew along the y-axis. - /// The - public static IImageProcessingContext Skew(this IImageProcessingContext source, float degreesX, float degreesY) - where TPixel : struct, IPixel - => Skew(source, degreesX, degreesY, KnownResamplers.Bicubic); + /// The . + public static IImageProcessingContext + Skew(this IImageProcessingContext source, float degreesX, float degreesY) => + Skew(source, degreesX, degreesY, KnownResamplers.Bicubic); /// /// Skews an image by the given angles in degrees using the specified sampling algorithm. /// - /// The pixel format. /// The image to skew. /// The angle in degrees to perform the skew along the x-axis. /// The angle in degrees to perform the skew along the y-axis. /// The to perform the resampling. - /// The - public static IImageProcessingContext Skew(this IImageProcessingContext source, float degreesX, float degreesY, IResampler sampler) - where TPixel : struct, IPixel - => source.ApplyProcessor(new SkewProcessor(degreesX, degreesY, sampler, source.GetCurrentSize())); + /// The . + public static IImageProcessingContext Skew( + this IImageProcessingContext source, + float degreesX, + float degreesY, + IResampler sampler) => + source.ApplyProcessor(new SkewProcessor(degreesX, degreesY, sampler, source.GetCurrentSize())); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/TransformExtensions.cs b/src/ImageSharp/Processing/TransformExtensions.cs index db14b6baf9..35f374d013 100644 --- a/src/ImageSharp/Processing/TransformExtensions.cs +++ b/src/ImageSharp/Processing/TransformExtensions.cs @@ -3,60 +3,53 @@ using System.Numerics; -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Transforms; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of composable transform operations to the type. + /// Adds extensions that allow the application of composable transform operations to the type. /// public static class TransformExtensions { /// /// Performs an affine transform of an image. /// - /// The pixel format. /// The image to transform. /// The affine transform builder. /// The - public static IImageProcessingContext Transform( - this IImageProcessingContext source, - AffineTransformBuilder builder) - where TPixel : struct, IPixel - => Transform(source, builder, KnownResamplers.Bicubic); + public static IImageProcessingContext Transform( + this IImageProcessingContext source, + AffineTransformBuilder builder) => + Transform(source, builder, KnownResamplers.Bicubic); /// /// Performs an affine transform of an image using the specified sampling algorithm. /// - /// The pixel format. - /// The . + /// The . /// The affine transform builder. /// The to perform the resampling. - /// The - public static IImageProcessingContext Transform( - this IImageProcessingContext ctx, + /// The . + public static IImageProcessingContext Transform( + this IImageProcessingContext ctx, AffineTransformBuilder builder, - IResampler sampler) - where TPixel : struct, IPixel - => ctx.Transform(new Rectangle(Point.Empty, ctx.GetCurrentSize()), builder, sampler); + IResampler sampler) => + ctx.Transform(new Rectangle(Point.Empty, ctx.GetCurrentSize()), builder, sampler); /// /// Performs an affine transform of an image using the specified sampling algorithm. /// - /// The pixel format. - /// The . + /// The . /// The source rectangle /// The affine transform builder. /// The to perform the resampling. - /// The - public static IImageProcessingContext Transform( - this IImageProcessingContext ctx, + /// The . + public static IImageProcessingContext Transform( + this IImageProcessingContext ctx, Rectangle sourceRectangle, AffineTransformBuilder builder, IResampler sampler) - where TPixel : struct, IPixel { Matrix3x2 transform = builder.BuildMatrix(sourceRectangle); Size targetDimensions = TransformUtils.GetTransformedSize(sourceRectangle.Size, transform); @@ -66,69 +59,61 @@ namespace SixLabors.ImageSharp.Processing /// /// Performs an affine transform of an image using the specified sampling algorithm. /// - /// The pixel format. - /// The . + /// The . /// The source rectangle /// The transformation matrix. /// The size of the result image. /// The to perform the resampling. - /// The - public static IImageProcessingContext Transform( - this IImageProcessingContext ctx, + /// The . + public static IImageProcessingContext Transform( + this IImageProcessingContext ctx, Rectangle sourceRectangle, Matrix3x2 transform, Size targetDimensions, IResampler sampler) - where TPixel : struct, IPixel { return ctx.ApplyProcessor( - new AffineTransformProcessor(transform, sampler, targetDimensions), + new AffineTransformProcessor(transform, sampler, targetDimensions), sourceRectangle); } /// /// Performs a projective transform of an image. /// - /// The pixel format. /// The image to transform. /// The affine transform builder. - /// The - public static IImageProcessingContext Transform( - this IImageProcessingContext source, - ProjectiveTransformBuilder builder) - where TPixel : struct, IPixel - => Transform(source, builder, KnownResamplers.Bicubic); + /// The . + public static IImageProcessingContext Transform( + this IImageProcessingContext source, + ProjectiveTransformBuilder builder) => + Transform(source, builder, KnownResamplers.Bicubic); /// /// Performs a projective transform of an image using the specified sampling algorithm. /// - /// The pixel format. - /// The . + /// The . /// The projective transform builder. /// The to perform the resampling. - /// The - public static IImageProcessingContext Transform( - this IImageProcessingContext ctx, + /// The . + public static IImageProcessingContext Transform( + this IImageProcessingContext ctx, ProjectiveTransformBuilder builder, - IResampler sampler) - where TPixel : struct, IPixel - => ctx.Transform(new Rectangle(Point.Empty, ctx.GetCurrentSize()), builder, sampler); + IResampler sampler) => + ctx.Transform(new Rectangle(Point.Empty, ctx.GetCurrentSize()), builder, sampler); /// /// Performs a projective transform of an image using the specified sampling algorithm. /// - /// The pixel format. - /// The . + /// The . /// The source rectangle /// The projective transform builder. /// The to perform the resampling. - /// The - public static IImageProcessingContext Transform( - this IImageProcessingContext ctx, + /// The . + public static IImageProcessingContext Transform( + this IImageProcessingContext ctx, Rectangle sourceRectangle, ProjectiveTransformBuilder builder, IResampler sampler) - where TPixel : struct, IPixel { Matrix4x4 transform = builder.BuildMatrix(sourceRectangle); Size targetDimensions = TransformUtils.GetTransformedSize(sourceRectangle.Size, transform); @@ -138,23 +123,21 @@ namespace SixLabors.ImageSharp.Processing /// /// Performs a projective transform of an image using the specified sampling algorithm. /// - /// The pixel format. - /// The . + /// The . /// The source rectangle /// The transformation matrix. /// The size of the result image. /// The to perform the resampling. - /// The - public static IImageProcessingContext Transform( - this IImageProcessingContext ctx, + /// The . + public static IImageProcessingContext Transform( + this IImageProcessingContext ctx, Rectangle sourceRectangle, Matrix4x4 transform, Size targetDimensions, IResampler sampler) - where TPixel : struct, IPixel { return ctx.ApplyProcessor( - new ProjectiveTransformProcessor(transform, sampler, targetDimensions), + new ProjectiveTransformProcessor(transform, sampler, targetDimensions), sourceRectangle); } } diff --git a/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs b/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs index b07f508834..ee04d43888 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs @@ -79,7 +79,8 @@ namespace SixLabors.ImageSharp.Tests .AppendTranslation(new PointF(10, 10)); // Apply a background color so we can see the translation. - blend.Mutate(x => x.Transform(builder).BackgroundColor(NamedColors.HotPink)); + blend.Mutate(x => x.Transform(builder)); + blend.Mutate(x => x.BackgroundColor(NamedColors.HotPink)); // Lets center the matrix so we can tell whether any cut-off issues we may have belong to the drawing processor var position = new Point((image.Width - blend.Width) / 2, (image.Height - blend.Height) / 2); diff --git a/tests/ImageSharp.Tests/Processing/Transforms/CropTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/CropTest.cs index 6731debd36..1c3bf2e90d 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/CropTest.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/CropTest.cs @@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms public void CropWidthHeightCropProcessorWithRectangleSet(int width, int height) { this.operations.Crop(width, height); - CropProcessor processor = this.Verify>(); + CropProcessor processor = this.Verify(); Assert.Equal(new Rectangle(0, 0, width, height), processor.CropRectangle); } @@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms { var cropRectangle = new Rectangle(x, y, width, height); this.operations.Crop(cropRectangle); - CropProcessor processor = this.Verify>(); + CropProcessor processor = this.Verify(); Assert.Equal(cropRectangle, processor.CropRectangle); } diff --git a/tests/ImageSharp.Tests/Processing/Transforms/EntropyCropTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/EntropyCropTest.cs index 03a8628a56..aa684acd55 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/EntropyCropTest.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/EntropyCropTest.cs @@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms public void EntropyCropThresholdFloatEntropyCropProcessorWithThreshold(float threshold) { this.operations.EntropyCrop(threshold); - EntropyCropProcessor processor = this.Verify>(); + EntropyCropProcessor processor = this.Verify(); Assert.Equal(threshold, processor.Threshold); } diff --git a/tests/ImageSharp.Tests/Processing/Transforms/FlipTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/FlipTests.cs index 39adcaa3fa..9fe2977adb 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/FlipTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/FlipTests.cs @@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms public void Flip_degreesFloat_RotateProcessorWithAnglesSetAndExpandTrue(FlipMode flip) { this.operations.Flip(flip); - FlipProcessor flipProcessor = this.Verify>(); + FlipProcessor flipProcessor = this.Verify(); Assert.Equal(flip, flipProcessor.FlipMode); } diff --git a/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs index 82d7682558..b870ddd08a 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs @@ -1,15 +1,13 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Transforms; + using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Transforms { - using SixLabors.ImageSharp.Processing; - using SixLabors.ImageSharp.Processing.Processors.Transforms; - public class PadTest : BaseImageOperationsExtensionTest { [Fact] @@ -20,7 +18,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms IResampler sampler = KnownResamplers.NearestNeighbor; this.operations.Pad(width, height); - ResizeProcessor resizeProcessor = this.Verify>(); + ResizeProcessor resizeProcessor = this.Verify(); Assert.Equal(width, resizeProcessor.Width); Assert.Equal(height, resizeProcessor.Height); diff --git a/tests/ImageSharp.Tests/Processing/Transforms/RotateFlipTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/RotateFlipTests.cs index dccf7afa6a..079ff67b7a 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/RotateFlipTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/RotateFlipTests.cs @@ -26,8 +26,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms public void RotateDegreesFloatRotateProcessorWithAnglesSet(RotateMode angle, FlipMode flip, float expectedAngle) { this.operations.RotateFlip(angle, flip); - RotateProcessor rotateProcessor = this.Verify>(0); - FlipProcessor flipProcessor = this.Verify>(1); + RotateProcessor rotateProcessor = this.Verify(0); + FlipProcessor flipProcessor = this.Verify(1); Assert.Equal(expectedAngle, rotateProcessor.Degrees); Assert.Equal(flip, flipProcessor.FlipMode); diff --git a/tests/ImageSharp.Tests/Processing/Transforms/RotateTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/RotateTests.cs index ae312d7235..3e2b859714 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/RotateTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/RotateTests.cs @@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms public void RotateDegreesFloatRotateProcessorWithAnglesSet(float angle) { this.operations.Rotate(angle); - RotateProcessor processor = this.Verify>(); + RotateProcessor processor = this.Verify(); Assert.Equal(angle, processor.Degrees); } @@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms public void RotateRotateTypeRotateProcessorWithAnglesConvertedFromEnum(RotateMode angle, float expectedAngle) { this.operations.Rotate(angle); // is this api needed ??? - RotateProcessor processor = this.Verify>(); + RotateProcessor processor = this.Verify(); Assert.Equal(expectedAngle, processor.Degrees); } diff --git a/tests/ImageSharp.Tests/Processing/Transforms/SkewTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/SkewTest.cs index 73754b9716..38033e80d0 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/SkewTest.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/SkewTest.cs @@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms public void SkewXYCreateSkewProcessorWithAnglesSet() { this.operations.Skew(10, 20); - SkewProcessor processor = this.Verify>(); + SkewProcessor processor = this.Verify(); Assert.Equal(10, processor.DegreesX); Assert.Equal(20, processor.DegreesY); diff --git a/tests/ImageSharp.Tests/Processing/Transforms/TransformsHelpersTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/TransformsHelpersTest.cs index a62f4fc7c6..3ac9af960b 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/TransformsHelpersTest.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/TransformsHelpersTest.cs @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms Assert.Equal(ExifDataType.Long, profile.GetValue(ExifTag.PixelXDimension).DataType); Assert.Equal(ExifDataType.Long, profile.GetValue(ExifTag.PixelYDimension).DataType); - TransformProcessorHelpers.UpdateDimensionalMetData(img); + TransformProcessorHelpers.UpdateDimensionalMetadata(img); Assert.Equal(ExifDataType.Short, profile.GetValue(ExifTag.PixelXDimension).DataType); Assert.Equal(ExifDataType.Short, profile.GetValue(ExifTag.PixelYDimension).DataType); From 22892a5cca31befa5796ed2297d22e2c34dc6aac Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 5 May 2019 02:36:41 +0200 Subject: [PATCH 131/223] sealed everything --- .../Processing/Processors/Convolution/BoxBlurProcessor.cs | 2 +- ...lution2DProcessor.cs => Convolution2DProcessor{TPixel}.cs} | 3 ++- ...2PassProcessor.cs => Convolution2PassProcessor{TPixel}.cs} | 0 ...onvolutionProcessor.cs => ConvolutionProcessor{TPixel}.cs} | 0 ...ector2DProcessor.cs => EdgeDetector2DProcessor{TPixel}.cs} | 0 ...ssProcessor.cs => EdgeDetectorCompassProcessor{TPixel}.cs} | 0 .../Processors/Convolution/GaussianBlurProcessor.cs | 2 +- .../Processors/Convolution/GaussianSharpenProcessor.cs | 2 +- .../Processing/Processors/Convolution/KayyaliProcessor.cs | 2 +- .../Processing/Processors/Convolution/KirschProcessor.cs | 2 +- .../Processors/Convolution/Laplacian3x3Processor.cs | 2 +- .../Processors/Convolution/Laplacian5x5Processor.cs | 2 +- .../Processors/Convolution/LaplacianOfGaussianProcessor.cs | 2 +- .../Processing/Processors/Convolution/PrewittProcessor.cs | 2 +- .../Processors/Convolution/RobertsCrossProcessor.cs | 2 +- .../Processing/Processors/Convolution/RobinsonProcessor.cs | 2 +- .../Processing/Processors/Convolution/ScharrProcessor.cs | 2 +- .../Processing/Processors/Convolution/SobelProcessor.cs | 2 +- .../Processing/Processors/Effects/OilPaintingProcessor.cs | 2 +- .../Processing/Processors/Effects/PixelateProcessor.cs | 2 +- .../Processing/Processors/Filters/AchromatomalyProcessor.cs | 4 +--- .../Processing/Processors/Filters/AchromatopsiaProcessor.cs | 4 +--- .../Processing/Processors/Filters/BlackWhiteProcessor.cs | 4 +--- .../Processing/Processors/Filters/BrightnessProcessor.cs | 4 +--- .../Processing/Processors/Filters/ContrastProcessor.cs | 4 +--- .../Processing/Processors/Filters/DeuteranomalyProcessor.cs | 4 +--- .../Processing/Processors/Filters/DeuteranopiaProcessor.cs | 4 +--- .../Processing/Processors/Filters/GrayscaleBt601Processor.cs | 4 +--- .../Processing/Processors/Filters/GrayscaleBt709Processor.cs | 2 +- src/ImageSharp/Processing/Processors/Filters/HueProcessor.cs | 4 +--- .../Processing/Processors/Filters/InvertProcessor.cs | 4 +--- .../Processing/Processors/Filters/KodachromeProcessor.cs | 2 +- .../Processing/Processors/Filters/LomographProcessor.cs | 2 +- .../Processing/Processors/Filters/OpacityProcessor.cs | 2 +- .../Processing/Processors/Filters/PolaroidProcessor.cs | 2 +- .../Processing/Processors/Filters/ProtanomalyProcessor.cs | 4 +--- .../Processing/Processors/Filters/ProtanopiaProcessor.cs | 4 +--- .../Processing/Processors/Filters/SaturateProcessor.cs | 4 +--- .../Processing/Processors/Filters/SepiaProcessor.cs | 4 +--- .../Processing/Processors/Filters/TritanomalyProcessor.cs | 4 +--- .../Processing/Processors/Filters/TritanopiaProcessor.cs | 4 +--- .../Processing/Processors/Transforms/FlipProcessor.cs | 2 +- .../Processing/Processors/Transforms/SkewProcessor.cs | 2 +- 43 files changed, 40 insertions(+), 71 deletions(-) rename src/ImageSharp/Processing/Processors/Convolution/{Convolution2DProcessor.cs => Convolution2DProcessor{TPixel}.cs} (99%) rename src/ImageSharp/Processing/Processors/Convolution/{Convolution2PassProcessor.cs => Convolution2PassProcessor{TPixel}.cs} (100%) rename src/ImageSharp/Processing/Processors/Convolution/{ConvolutionProcessor.cs => ConvolutionProcessor{TPixel}.cs} (100%) rename src/ImageSharp/Processing/Processors/Convolution/{EdgeDetector2DProcessor.cs => EdgeDetector2DProcessor{TPixel}.cs} (100%) rename src/ImageSharp/Processing/Processors/Convolution/{EdgeDetectorCompassProcessor.cs => EdgeDetectorCompassProcessor{TPixel}.cs} (100%) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs index bb37489e13..4e56e75d39 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Defines a box blur processor of a given Radius. /// - public class BoxBlurProcessor : IImageProcessor + public sealed class BoxBlurProcessor : IImageProcessor { /// /// The default radius used by the parameterless constructor. diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs similarity index 99% rename from src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs rename to src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs index 633b50a9b7..299b1d41c1 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs @@ -1,9 +1,10 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; using System.Numerics; using System.Runtime.InteropServices; + using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs rename to src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs rename to src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs rename to src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs rename to src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs index c64a024f9f..764f4ca517 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Defines Gaussian blur by a (Sigma, Radius) pair. /// - public class GaussianBlurProcessor : IImageProcessor + public sealed class GaussianBlurProcessor : IImageProcessor { /// /// The default value for . diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs index 84e56869d3..23282af36d 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Defines Gaussian sharpening by a (Sigma, Radius) pair. /// - public class GaussianSharpenProcessor : IImageProcessor + public sealed class GaussianSharpenProcessor : IImageProcessor { /// /// The default value for . diff --git a/src/ImageSharp/Processing/Processors/Convolution/KayyaliProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/KayyaliProcessor.cs index 99dfb17ffb..2d0f056b61 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/KayyaliProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/KayyaliProcessor.cs @@ -7,7 +7,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Defines edge detection processing using the Kayyali operator filter. /// See . /// - public class KayyaliProcessor : EdgeDetectorProcessor + public sealed class KayyaliProcessor : EdgeDetectorProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Convolution/KirschProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/KirschProcessor.cs index fc73903c7d..9e95344222 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/KirschProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/KirschProcessor.cs @@ -7,7 +7,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Defines edge detection using the Kirsch operator filter. /// See . /// - public class KirschProcessor : EdgeDetectorProcessor + public sealed class KirschProcessor : EdgeDetectorProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Convolution/Laplacian3x3Processor.cs b/src/ImageSharp/Processing/Processors/Convolution/Laplacian3x3Processor.cs index 18a53cb131..9c9488fec0 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Laplacian3x3Processor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Laplacian3x3Processor.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Applies edge detection processing to the image using the Laplacian 3x3 operator filter. /// /// - public class Laplacian3x3Processor : EdgeDetectorProcessor + public sealed class Laplacian3x3Processor : EdgeDetectorProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Convolution/Laplacian5x5Processor.cs b/src/ImageSharp/Processing/Processors/Convolution/Laplacian5x5Processor.cs index 1c95ca4903..fa0c8c5aa3 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Laplacian5x5Processor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Laplacian5x5Processor.cs @@ -7,7 +7,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Defines edge detection processing using the Laplacian 5x5 operator filter. /// . /// - public class Laplacian5x5Processor : EdgeDetectorProcessor + public sealed class Laplacian5x5Processor : EdgeDetectorProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Convolution/LaplacianOfGaussianProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/LaplacianOfGaussianProcessor.cs index d904d69b1b..2caff8201c 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/LaplacianOfGaussianProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/LaplacianOfGaussianProcessor.cs @@ -7,7 +7,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Applies edge detection processing to the image using the Laplacian of Gaussian operator filter. /// See . /// - public class LaplacianOfGaussianProcessor : EdgeDetectorProcessor + public sealed class LaplacianOfGaussianProcessor : EdgeDetectorProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Convolution/PrewittProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/PrewittProcessor.cs index 939b1e0c52..29f6fc279c 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/PrewittProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/PrewittProcessor.cs @@ -7,7 +7,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Defines edge detection using the Prewitt operator filter. /// See . /// - public class PrewittProcessor : EdgeDetectorProcessor + public sealed class PrewittProcessor : EdgeDetectorProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Convolution/RobertsCrossProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/RobertsCrossProcessor.cs index ff41b6c694..a9db37a076 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/RobertsCrossProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/RobertsCrossProcessor.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Defines edge detection processing using the Roberts Cross operator filter. /// See . /// - public class RobertsCrossProcessor : EdgeDetectorProcessor + public sealed class RobertsCrossProcessor : EdgeDetectorProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Convolution/RobinsonProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/RobinsonProcessor.cs index 603a81f676..fd73789027 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/RobinsonProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/RobinsonProcessor.cs @@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Defines edge detection using the Robinson operator filter. /// See . /// - internal class RobinsonProcessor : EdgeDetectorProcessor + public sealed class RobinsonProcessor : EdgeDetectorProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Convolution/ScharrProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/ScharrProcessor.cs index d23ba53384..ec0183dc63 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/ScharrProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/ScharrProcessor.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Defines edge detection processing using the Scharr operator filter. /// /// - public class ScharrProcessor : EdgeDetectorProcessor + public sealed class ScharrProcessor : EdgeDetectorProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Convolution/SobelProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/SobelProcessor.cs index 157bb62e50..bc4339e052 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/SobelProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/SobelProcessor.cs @@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Defines edge detection using the Sobel operator filter. /// See . /// - public class SobelProcessor : EdgeDetectorProcessor + public sealed class SobelProcessor : EdgeDetectorProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs index 14e164fb77..741ba9eced 100644 --- a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects /// /// Defines an oil painting effect. /// - public class OilPaintingProcessor : IImageProcessor + public sealed class OilPaintingProcessor : IImageProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs index eeb2aafcb5..1599c9dab8 100644 --- a/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects /// /// Defines a pixelation effect of a given size. /// - public class PixelateProcessor : IImageProcessor + public sealed class PixelateProcessor : IImageProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/AchromatomalyProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/AchromatomalyProcessor.cs index 7c3789c388..0e6653d99b 100644 --- a/src/ImageSharp/Processing/Processors/Filters/AchromatomalyProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/AchromatomalyProcessor.cs @@ -1,14 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// /// Converts the colors of the image recreating Achromatomaly (Color desensitivity) color blindness. /// - public class AchromatomalyProcessor : FilterProcessor + public sealed class AchromatomalyProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/AchromatopsiaProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/AchromatopsiaProcessor.cs index ab04a30ba1..10a201b1d7 100644 --- a/src/ImageSharp/Processing/Processors/Filters/AchromatopsiaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/AchromatopsiaProcessor.cs @@ -1,14 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// /// Converts the colors of the image recreating Achromatopsia (Monochrome) color blindness. /// - public class AchromatopsiaProcessor : FilterProcessor + public sealed class AchromatopsiaProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/BlackWhiteProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/BlackWhiteProcessor.cs index 7e2e7f2f47..425ae511f6 100644 --- a/src/ImageSharp/Processing/Processors/Filters/BlackWhiteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/BlackWhiteProcessor.cs @@ -1,14 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// /// Applies a black and white filter matrix to the image. /// - public class BlackWhiteProcessor : FilterProcessor + public sealed class BlackWhiteProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/BrightnessProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/BrightnessProcessor.cs index 94a7a394a6..77862b3e3f 100644 --- a/src/ImageSharp/Processing/Processors/Filters/BrightnessProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/BrightnessProcessor.cs @@ -1,14 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// /// Applies a brightness filter matrix using the given amount. /// - public class BrightnessProcessor : FilterProcessor + public sealed class BrightnessProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/ContrastProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/ContrastProcessor.cs index 1f74a1d642..3fdeafde3e 100644 --- a/src/ImageSharp/Processing/Processors/Filters/ContrastProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/ContrastProcessor.cs @@ -1,14 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// /// Applies a contrast filter matrix using the given amount. /// - public class ContrastProcessor : FilterProcessor + public sealed class ContrastProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/DeuteranomalyProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/DeuteranomalyProcessor.cs index 7ad0630aac..7e1c0ca503 100644 --- a/src/ImageSharp/Processing/Processors/Filters/DeuteranomalyProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/DeuteranomalyProcessor.cs @@ -1,14 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// /// Converts the colors of the image recreating Deuteranomaly (Green-Weak) color blindness. /// - public class DeuteranomalyProcessor : FilterProcessor + public sealed class DeuteranomalyProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/DeuteranopiaProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/DeuteranopiaProcessor.cs index 188d02dc8b..e4ed44e74e 100644 --- a/src/ImageSharp/Processing/Processors/Filters/DeuteranopiaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/DeuteranopiaProcessor.cs @@ -1,14 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// /// Converts the colors of the image recreating Deuteranopia (Green-Blind) color blindness. /// - public class DeuteranopiaProcessor : FilterProcessor + public sealed class DeuteranopiaProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt601Processor.cs b/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt601Processor.cs index c24992b441..153a1a17c5 100644 --- a/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt601Processor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt601Processor.cs @@ -1,14 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// /// Applies a grayscale filter matrix using the given amount and the formula as specified by ITU-R Recommendation BT.601 /// - public class GrayscaleBt601Processor : FilterProcessor + public sealed class GrayscaleBt601Processor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs b/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs index dc5294ccf0..2d7d2a1eaa 100644 --- a/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies a grayscale filter matrix using the given amount and the formula as specified by ITU-R Recommendation BT.709 /// - public class GrayscaleBt709Processor : FilterProcessor + public sealed class GrayscaleBt709Processor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/HueProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/HueProcessor.cs index 65f7eef330..05d3cbdc7c 100644 --- a/src/ImageSharp/Processing/Processors/Filters/HueProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/HueProcessor.cs @@ -1,14 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// /// Applies a hue filter matrix using the given angle of rotation in degrees /// - public class HueProcessor : FilterProcessor + public sealed class HueProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/InvertProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/InvertProcessor.cs index cc41e65721..99b138033d 100644 --- a/src/ImageSharp/Processing/Processors/Filters/InvertProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/InvertProcessor.cs @@ -1,14 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// /// Applies a filter matrix that inverts the colors of an image /// - public class InvertProcessor : FilterProcessor + public sealed class InvertProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/KodachromeProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/KodachromeProcessor.cs index a5215a434c..012b10ee03 100644 --- a/src/ImageSharp/Processing/Processors/Filters/KodachromeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/KodachromeProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies a filter matrix recreating an old Kodachrome camera effect matrix to the image /// - public class KodachromeProcessor : FilterProcessor + public sealed class KodachromeProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs index dae168e2cb..f5a1befffb 100644 --- a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating an old Lomograph effect. /// - public class LomographProcessor : FilterProcessor + public sealed class LomographProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs index 0ee5dd59e5..922ca32330 100644 --- a/src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies an opacity filter matrix using the given amount. /// - public class OpacityProcessor : FilterProcessor + public sealed class OpacityProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs index 341c02acfc..676bbc06bb 100644 --- a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating an old Polaroid effect. /// - public class PolaroidProcessor : FilterProcessor + public sealed class PolaroidProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/ProtanomalyProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/ProtanomalyProcessor.cs index 755bc0f81a..1c01b608e5 100644 --- a/src/ImageSharp/Processing/Processors/Filters/ProtanomalyProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/ProtanomalyProcessor.cs @@ -1,14 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// /// Converts the colors of the image recreating Protanomaly (Red-Weak) color blindness. /// - public class ProtanomalyProcessor : FilterProcessor + public sealed class ProtanomalyProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/ProtanopiaProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/ProtanopiaProcessor.cs index c4f4a76611..ec423bd9a5 100644 --- a/src/ImageSharp/Processing/Processors/Filters/ProtanopiaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/ProtanopiaProcessor.cs @@ -1,14 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// /// Converts the colors of the image recreating Protanopia (Red-Blind) color blindness. /// - public class ProtanopiaProcessor : FilterProcessor + public sealed class ProtanopiaProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/SaturateProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/SaturateProcessor.cs index 770fb98277..2cc40664b4 100644 --- a/src/ImageSharp/Processing/Processors/Filters/SaturateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/SaturateProcessor.cs @@ -1,14 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// /// Applies a saturation filter matrix using the given amount. /// - public class SaturateProcessor : FilterProcessor + public sealed class SaturateProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/SepiaProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/SepiaProcessor.cs index 9da48993a2..34af410671 100644 --- a/src/ImageSharp/Processing/Processors/Filters/SepiaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/SepiaProcessor.cs @@ -1,14 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// /// Applies a sepia filter matrix using the given amount. /// - public class SepiaProcessor : FilterProcessor + public sealed class SepiaProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/TritanomalyProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/TritanomalyProcessor.cs index c64475b994..a31e52c7e7 100644 --- a/src/ImageSharp/Processing/Processors/Filters/TritanomalyProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/TritanomalyProcessor.cs @@ -1,14 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// /// Converts the colors of the image recreating Tritanomaly (Blue-Weak) color blindness. /// - public class TritanomalyProcessor : FilterProcessor + public sealed class TritanomalyProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/TritanopiaProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/TritanopiaProcessor.cs index 722840e743..b622126f32 100644 --- a/src/ImageSharp/Processing/Processors/Filters/TritanopiaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/TritanopiaProcessor.cs @@ -1,14 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// /// Converts the colors of the image recreating Tritanopia (Blue-Blind) color blindness. /// - public class TritanopiaProcessor : FilterProcessor + public sealed class TritanopiaProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs index 9a3eab6cc8..f604d8399f 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// /// Defines a flipping around the center point of the image. /// - public class FlipProcessor : IImageProcessor + public sealed class FlipProcessor : IImageProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs index 35c3717eea..cb73bb66c2 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// /// Defines a skew transformation applicable to an . /// - internal class SkewProcessor : AffineTransformProcessor + public sealed class SkewProcessor : AffineTransformProcessor { /// /// Initializes a new instance of the class. From 394174bb532ad4f61b35b81cbfa38b9131535158 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 5 May 2019 15:56:34 +0200 Subject: [PATCH 132/223] refactor HistogramEqualization --- .../HistogramEqualizationExtension.cs | 44 +- .../AdaptiveHistEqualizationProcessor.cs | 526 +---------------- ...aptiveHistEqualizationProcessor{TPixel}.cs | 546 ++++++++++++++++++ .../AdaptiveHistEqualizationSWProcessor.cs | 371 +----------- ...tiveHistEqualizationSWProcessor{TPixel}.cs | 390 +++++++++++++ .../GlobalHistogramEqualizationProcessor.cs | 99 +--- ...lHistogramEqualizationProcessor{TPixel}.cs | 107 ++++ .../HistogramEqualizationOptions.cs | 2 +- .../HistogramEqualizationProcessor.cs | 134 ++--- .../HistogramEqualizationProcessor{TPixel}.cs | 139 +++++ .../HistogramEqualizationTests.cs | 4 +- .../Processors/Dithering/DitherTests.cs | 3 +- 12 files changed, 1264 insertions(+), 1101 deletions(-) create mode 100644 src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor{TPixel}.cs create mode 100644 src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor{TPixel}.cs create mode 100644 src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs create mode 100644 src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor{TPixel}.cs diff --git a/src/ImageSharp/Processing/HistogramEqualizationExtension.cs b/src/ImageSharp/Processing/HistogramEqualizationExtension.cs index d967ef3622..aae3e997fe 100644 --- a/src/ImageSharp/Processing/HistogramEqualizationExtension.cs +++ b/src/ImageSharp/Processing/HistogramEqualizationExtension.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Normalization; namespace SixLabors.ImageSharp.Processing @@ -14,49 +13,20 @@ namespace SixLabors.ImageSharp.Processing /// /// Equalizes the histogram of an image to increases the contrast. /// - /// The pixel format. /// The image this method extends. /// The . - public static IImageProcessingContext HistogramEqualization(this IImageProcessingContext source) - where TPixel : struct, IPixel - => HistogramEqualization(source, HistogramEqualizationOptions.Default); + public static IImageProcessingContext HistogramEqualization(this IImageProcessingContext source) => + HistogramEqualization(source, HistogramEqualizationOptions.Default); /// /// Equalizes the histogram of an image to increases the contrast. /// - /// The pixel format. /// The image this method extends. /// The histogram equalization options to use. /// The . - public static IImageProcessingContext HistogramEqualization(this IImageProcessingContext source, HistogramEqualizationOptions options) - where TPixel : struct, IPixel - => source.ApplyProcessor(GetProcessor(options)); - - private static HistogramEqualizationProcessor GetProcessor(HistogramEqualizationOptions options) - where TPixel : struct, IPixel - { - HistogramEqualizationProcessor processor; - - switch (options.Method) - { - case HistogramEqualizationMethod.Global: - processor = new GlobalHistogramEqualizationProcessor(options.LuminanceLevels, options.ClipHistogram, options.ClipLimitPercentage); - break; - - case HistogramEqualizationMethod.AdaptiveTileInterpolation: - processor = new AdaptiveHistEqualizationProcessor(options.LuminanceLevels, options.ClipHistogram, options.ClipLimitPercentage, options.Tiles); - break; - - case HistogramEqualizationMethod.AdaptiveSlidingWindow: - processor = new AdaptiveHistEqualizationSWProcessor(options.LuminanceLevels, options.ClipHistogram, options.ClipLimitPercentage, options.Tiles); - break; - - default: - processor = new GlobalHistogramEqualizationProcessor(options.LuminanceLevels, options.ClipHistogram, options.ClipLimitPercentage); - break; - } - - return processor; - } + public static IImageProcessingContext HistogramEqualization( + this IImageProcessingContext source, + HistogramEqualizationOptions options) => + source.ApplyProcessor(HistogramEqualizationProcessor.FromOptions(options)); } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs index cb52a88b7b..4fd0f853d0 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs @@ -1,545 +1,37 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; -using System.Buffers; -using System.Collections.Generic; -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Threading.Tasks; -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; -using SixLabors.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Normalization { /// /// Applies an adaptive histogram equalization to the image. The image is split up in tiles. For each tile a cumulative distribution function (cdf) is calculated. /// To calculate the final equalized pixel value, the cdf value of four adjacent tiles will be interpolated. /// - /// The pixel format. - internal class AdaptiveHistEqualizationProcessor : HistogramEqualizationProcessor - where TPixel : struct, IPixel + internal class AdaptiveHistEqualizationProcessor : HistogramEqualizationProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images /// or 65536 for 16-bit grayscale images. /// Indicating whether to clip the histogram bins at a specific value. /// Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. - /// The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100. - public AdaptiveHistEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage, int tiles) + /// The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100. + public AdaptiveHistEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage, int numberOfTiles) : base(luminanceLevels, clipHistogram, clipLimitPercentage) { - Guard.MustBeGreaterThanOrEqualTo(tiles, 2, nameof(tiles)); - Guard.MustBeLessThanOrEqualTo(tiles, 100, nameof(tiles)); - - this.Tiles = tiles; + this.NumberOfTiles = numberOfTiles; } /// /// Gets the number of tiles the image is split into (horizontal and vertically) for the adaptive histogram equalization. /// - private int Tiles { get; } - - /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - { - int sourceWidth = source.Width; - int sourceHeight = source.Height; - int numberOfPixels = sourceWidth * sourceHeight; - int tileWidth = (int)MathF.Ceiling(sourceWidth / (float)this.Tiles); - int tileHeight = (int)MathF.Ceiling(sourceHeight / (float)this.Tiles); - int pixelsInTile = tileWidth * tileHeight; - int halfTileWidth = tileWidth / 2; - int halfTileHeight = tileHeight / 2; - int luminanceLevels = this.LuminanceLevels; - - // The image is split up into tiles. For each tile the cumulative distribution function will be calculated. - using (var cdfData = new CdfTileData(configuration, sourceWidth, sourceHeight, this.Tiles, this.Tiles, tileWidth, tileHeight, luminanceLevels)) - { - cdfData.CalculateLookupTables(source, this); - - var tileYStartPositions = new List<(int y, int cdfY)>(); - int cdfY = 0; - for (int y = halfTileHeight; y < sourceHeight - halfTileHeight; y += tileHeight) - { - tileYStartPositions.Add((y, cdfY)); - cdfY++; - } - - Parallel.For( - 0, - tileYStartPositions.Count, - new ParallelOptions() { MaxDegreeOfParallelism = configuration.MaxDegreeOfParallelism }, - index => - { - int cdfX = 0; - int tileX = 0; - int tileY = 0; - int y = tileYStartPositions[index].y; - int cdfYY = tileYStartPositions[index].cdfY; - - // It's unfortunate that we have to do this per iteration. - ref TPixel sourceBase = ref source.GetPixelReference(0, 0); - - cdfX = 0; - for (int x = halfTileWidth; x < sourceWidth - halfTileWidth; x += tileWidth) - { - tileY = 0; - int yEnd = Math.Min(y + tileHeight, sourceHeight); - int xEnd = Math.Min(x + tileWidth, sourceWidth); - for (int dy = y; dy < yEnd; dy++) - { - int dyOffSet = dy * sourceWidth; - tileX = 0; - for (int dx = x; dx < xEnd; dx++) - { - ref TPixel pixel = ref Unsafe.Add(ref sourceBase, dyOffSet + dx); - float luminanceEqualized = InterpolateBetweenFourTiles( - pixel, - cdfData, - this.Tiles, - this.Tiles, - tileX, - tileY, - cdfX, - cdfYY, - tileWidth, - tileHeight, - luminanceLevels); - - pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); - tileX++; - } - - tileY++; - } - - cdfX++; - } - }); - - ref TPixel pixelsBase = ref source.GetPixelReference(0, 0); - - // Fix left column - ProcessBorderColumn(ref pixelsBase, cdfData, 0, sourceWidth, sourceHeight, tileWidth, tileHeight, xStart: 0, xEnd: halfTileWidth, luminanceLevels); - - // Fix right column - int rightBorderStartX = ((this.Tiles - 1) * tileWidth) + halfTileWidth; - ProcessBorderColumn(ref pixelsBase, cdfData, this.Tiles - 1, sourceWidth, sourceHeight, tileWidth, tileHeight, xStart: rightBorderStartX, xEnd: sourceWidth, luminanceLevels); - - // Fix top row - ProcessBorderRow(ref pixelsBase, cdfData, 0, sourceWidth, tileWidth, yStart: 0, yEnd: halfTileHeight, luminanceLevels); - - // Fix bottom row - int bottomBorderStartY = ((this.Tiles - 1) * tileHeight) + halfTileHeight; - ProcessBorderRow(ref pixelsBase, cdfData, this.Tiles - 1, sourceWidth, tileWidth, yStart: bottomBorderStartY, yEnd: sourceHeight, luminanceLevels); - - // Left top corner - ProcessCornerTile(ref pixelsBase, cdfData, sourceWidth, 0, 0, xStart: 0, xEnd: halfTileWidth, yStart: 0, yEnd: halfTileHeight, luminanceLevels); - - // Left bottom corner - ProcessCornerTile(ref pixelsBase, cdfData, sourceWidth, 0, this.Tiles - 1, xStart: 0, xEnd: halfTileWidth, yStart: bottomBorderStartY, yEnd: sourceHeight, luminanceLevels); - - // Right top corner - ProcessCornerTile(ref pixelsBase, cdfData, sourceWidth, this.Tiles - 1, 0, xStart: rightBorderStartX, xEnd: sourceWidth, yStart: 0, yEnd: halfTileHeight, luminanceLevels); - - // Right bottom corner - ProcessCornerTile(ref pixelsBase, cdfData, sourceWidth, this.Tiles - 1, this.Tiles - 1, xStart: rightBorderStartX, xEnd: sourceWidth, yStart: bottomBorderStartY, yEnd: sourceHeight, luminanceLevels); - } - } - - /// - /// Processes the part of a corner tile which was previously left out. It consists of 1 / 4 of a tile and does not need interpolation. - /// - /// The output pixels base reference. - /// The lookup table to remap the grey values. - /// The source image width. - /// The x-position in the CDF lookup map. - /// The y-position in the CDF lookup map. - /// X start position. - /// X end position. - /// Y start position. - /// Y end position. - /// - /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images - /// or 65536 for 16-bit grayscale images. - /// - private static void ProcessCornerTile( - ref TPixel pixelsBase, - CdfTileData cdfData, - int sourceWidth, - int cdfX, - int cdfY, - int xStart, - int xEnd, - int yStart, - int yEnd, - int luminanceLevels) - { - for (int dy = yStart; dy < yEnd; dy++) - { - int dyOffSet = dy * sourceWidth; - for (int dx = xStart; dx < xEnd; dx++) - { - ref TPixel pixel = ref Unsafe.Add(ref pixelsBase, dyOffSet + dx); - float luminanceEqualized = cdfData.RemapGreyValue(cdfX, cdfY, GetLuminance(pixel, luminanceLevels)); - pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); - } - } - } - - /// - /// Processes a border column of the image which is half the size of the tile width. - /// - /// The output pixels reference. - /// The pre-computed lookup tables to remap the grey values for each tiles. - /// The X index of the lookup table to use. - /// The source image width. - /// The source image height. - /// The width of a tile. - /// The height of a tile. - /// X start position in the image. - /// X end position of the image. - /// - /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images - /// or 65536 for 16-bit grayscale images. - /// - private static void ProcessBorderColumn( - ref TPixel pixelBase, - CdfTileData cdfData, - int cdfX, - int sourceWidth, - int sourceHeight, - int tileWidth, - int tileHeight, - int xStart, - int xEnd, - int luminanceLevels) - { - int halfTileWidth = tileWidth / 2; - int halfTileHeight = tileHeight / 2; - - int cdfY = 0; - for (int y = halfTileHeight; y < sourceHeight - halfTileHeight; y += tileHeight) - { - int yLimit = Math.Min(y + tileHeight, sourceHeight - 1); - int tileY = 0; - for (int dy = y; dy < yLimit; dy++) - { - int dyOffSet = dy * sourceWidth; - int tileX = halfTileWidth; - for (int dx = xStart; dx < xEnd; dx++) - { - ref TPixel pixel = ref Unsafe.Add(ref pixelBase, dyOffSet + dx); - float luminanceEqualized = InterpolateBetweenTwoTiles(pixel, cdfData, cdfX, cdfY, cdfX, cdfY + 1, tileY, tileHeight, luminanceLevels); - pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); - tileX++; - } - - tileY++; - } - - cdfY++; - } - } + public int NumberOfTiles { get; } - /// - /// Processes a border row of the image which is half of the size of the tile height. - /// - /// The output pixels base reference. - /// The pre-computed lookup tables to remap the grey values for each tiles. - /// The Y index of the lookup table to use. - /// The source image width. - /// The width of a tile. - /// Y start position in the image. - /// Y end position of the image. - /// - /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images - /// or 65536 for 16-bit grayscale images. - /// - private static void ProcessBorderRow( - ref TPixel pixelBase, - CdfTileData cdfData, - int cdfY, - int sourceWidth, - int tileWidth, - int yStart, - int yEnd, - int luminanceLevels) + /// + public override IImageProcessor CreatePixelSpecificProcessor() { - int halfTileWidth = tileWidth / 2; - - int cdfX = 0; - for (int x = halfTileWidth; x < sourceWidth - halfTileWidth; x += tileWidth) - { - int tileY = 0; - for (int dy = yStart; dy < yEnd; dy++) - { - int dyOffSet = dy * sourceWidth; - int tileX = 0; - int xLimit = Math.Min(x + tileWidth, sourceWidth - 1); - for (int dx = x; dx < xLimit; dx++) - { - ref TPixel pixel = ref Unsafe.Add(ref pixelBase, dyOffSet + dx); - float luminanceEqualized = InterpolateBetweenTwoTiles(pixel, cdfData, cdfX, cdfY, cdfX + 1, cdfY, tileX, tileWidth, luminanceLevels); - pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); - tileX++; - } - - tileY++; - } - - cdfX++; - } - } - - /// - /// Bilinear interpolation between four adjacent tiles. - /// - /// The pixel to remap the grey value from. - /// The pre-computed lookup tables to remap the grey values for each tiles. - /// The number of tiles in the x-direction. - /// The number of tiles in the y-direction. - /// X position inside the tile. - /// Y position inside the tile. - /// X index of the top left lookup table to use. - /// Y index of the top left lookup table to use. - /// Width of one tile in pixels. - /// Height of one tile in pixels. - /// - /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images - /// or 65536 for 16-bit grayscale images. - /// - /// A re-mapped grey value. - [MethodImpl(InliningOptions.ShortMethod)] - private static float InterpolateBetweenFourTiles( - TPixel sourcePixel, - CdfTileData cdfData, - int tileCountX, - int tileCountY, - int tileX, - int tileY, - int cdfX, - int cdfY, - int tileWidth, - int tileHeight, - int luminanceLevels) - { - int luminance = GetLuminance(sourcePixel, luminanceLevels); - float tx = tileX / (float)(tileWidth - 1); - float ty = tileY / (float)(tileHeight - 1); - - int yTop = cdfY; - int yBottom = Math.Min(tileCountY - 1, yTop + 1); - int xLeft = cdfX; - int xRight = Math.Min(tileCountX - 1, xLeft + 1); - - float cdfLeftTopLuminance = cdfData.RemapGreyValue(xLeft, yTop, luminance); - float cdfRightTopLuminance = cdfData.RemapGreyValue(xRight, yTop, luminance); - float cdfLeftBottomLuminance = cdfData.RemapGreyValue(xLeft, yBottom, luminance); - float cdfRightBottomLuminance = cdfData.RemapGreyValue(xRight, yBottom, luminance); - return BilinearInterpolation(tx, ty, cdfLeftTopLuminance, cdfRightTopLuminance, cdfLeftBottomLuminance, cdfRightBottomLuminance); - } - - /// - /// Linear interpolation between two tiles. - /// - /// The pixel to remap the grey value from. - /// The CDF lookup map. - /// X position inside the first tile. - /// Y position inside the first tile. - /// X position inside the second tile. - /// Y position inside the second tile. - /// Position inside the tile. - /// Width of the tile. - /// - /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images - /// or 65536 for 16-bit grayscale images. - /// - /// A re-mapped grey value. - [MethodImpl(InliningOptions.ShortMethod)] - private static float InterpolateBetweenTwoTiles( - TPixel sourcePixel, - CdfTileData cdfData, - int tileX1, - int tileY1, - int tileX2, - int tileY2, - int tilePos, - int tileWidth, - int luminanceLevels) - { - int luminance = GetLuminance(sourcePixel, luminanceLevels); - float tx = tilePos / (float)(tileWidth - 1); - - float cdfLuminance1 = cdfData.RemapGreyValue(tileX1, tileY1, luminance); - float cdfLuminance2 = cdfData.RemapGreyValue(tileX2, tileY2, luminance); - return LinearInterpolation(cdfLuminance1, cdfLuminance2, tx); - } - - /// - /// Bilinear interpolation between four tiles. - /// - /// The interpolation value in x direction in the range of [0, 1]. - /// The interpolation value in y direction in the range of [0, 1]. - /// Luminance from top left tile. - /// Luminance from right top tile. - /// Luminance from left bottom tile. - /// Luminance from right bottom tile. - /// Interpolated Luminance. - [MethodImpl(InliningOptions.ShortMethod)] - private static float BilinearInterpolation(float tx, float ty, float lt, float rt, float lb, float rb) - => LinearInterpolation(LinearInterpolation(lt, rt, tx), LinearInterpolation(lb, rb, tx), ty); - - /// - /// Linear interpolation between two grey values. - /// - /// The left value. - /// The right value. - /// The interpolation value between the two values in the range of [0, 1]. - /// The interpolated value. - [MethodImpl(InliningOptions.ShortMethod)] - private static float LinearInterpolation(float left, float right, float t) - => left + ((right - left) * t); - - /// - /// Contains the results of the cumulative distribution function for all tiles. - /// - private sealed class CdfTileData : IDisposable - { - private readonly Configuration configuration; - private readonly MemoryAllocator memoryAllocator; - - // Used for storing the minimum value for each CDF entry. - private readonly Buffer2D cdfMinBuffer2D; - - // Used for storing the LUT for each CDF entry. - private readonly Buffer2D cdfLutBuffer2D; - private readonly int pixelsInTile; - private readonly int sourceWidth; - private readonly int sourceHeight; - private readonly int tileWidth; - private readonly int tileHeight; - private readonly int luminanceLevels; - private readonly List<(int y, int cdfY)> tileYStartPositions; - - public CdfTileData( - Configuration configuration, - int sourceWidth, - int sourceHeight, - int tileCountX, - int tileCountY, - int tileWidth, - int tileHeight, - int luminanceLevels) - { - this.configuration = configuration; - this.memoryAllocator = configuration.MemoryAllocator; - this.luminanceLevels = luminanceLevels; - this.cdfMinBuffer2D = this.memoryAllocator.Allocate2D(tileCountX, tileCountY); - this.cdfLutBuffer2D = this.memoryAllocator.Allocate2D(tileCountX * luminanceLevels, tileCountY); - this.sourceWidth = sourceWidth; - this.sourceHeight = sourceHeight; - this.tileWidth = tileWidth; - this.tileHeight = tileHeight; - this.pixelsInTile = tileWidth * tileHeight; - - // Calculate the start positions and rent buffers. - this.tileYStartPositions = new List<(int y, int cdfY)>(); - int cdfY = 0; - for (int y = 0; y < sourceHeight; y += tileHeight) - { - this.tileYStartPositions.Add((y, cdfY)); - cdfY++; - } - } - - public void CalculateLookupTables(ImageFrame source, HistogramEqualizationProcessor processor) - { - int sourceWidth = this.sourceWidth; - int sourceHeight = this.sourceHeight; - int tileWidth = this.tileWidth; - int tileHeight = this.tileHeight; - int luminanceLevels = this.luminanceLevels; - MemoryAllocator memoryAllocator = this.memoryAllocator; - - Parallel.For( - 0, - this.tileYStartPositions.Count, - new ParallelOptions() { MaxDegreeOfParallelism = this.configuration.MaxDegreeOfParallelism }, - index => - { - int cdfX = 0; - int cdfY = this.tileYStartPositions[index].cdfY; - int y = this.tileYStartPositions[index].y; - int endY = Math.Min(y + tileHeight, sourceHeight); - ref TPixel sourceBase = ref source.GetPixelReference(0, 0); - ref int cdfMinBase = ref MemoryMarshal.GetReference(this.cdfMinBuffer2D.GetRowSpan(cdfY)); - - using (IMemoryOwner histogramBuffer = this.memoryAllocator.Allocate(luminanceLevels)) - { - Span histogram = histogramBuffer.GetSpan(); - ref int histogramBase = ref MemoryMarshal.GetReference(histogram); - - for (int x = 0; x < sourceWidth; x += tileWidth) - { - histogram.Clear(); - ref int cdfBase = ref MemoryMarshal.GetReference(this.GetCdfLutSpan(cdfX, index)); - - int xlimit = Math.Min(x + tileWidth, sourceWidth); - for (int dy = y; dy < endY; dy++) - { - int dyOffset = dy * sourceWidth; - for (int dx = x; dx < xlimit; dx++) - { - int luminace = GetLuminance(Unsafe.Add(ref sourceBase, dyOffset + dx), luminanceLevels); - histogram[luminace]++; - } - } - - if (processor.ClipHistogramEnabled) - { - processor.ClipHistogram(histogram, processor.ClipLimitPercentage, this.pixelsInTile); - } - - Unsafe.Add(ref cdfMinBase, cdfX) = processor.CalculateCdf(ref cdfBase, ref histogramBase, histogram.Length - 1); - - cdfX++; - } - } - }); - } - - [MethodImpl(InliningOptions.ShortMethod)] - public Span GetCdfLutSpan(int tileX, int tileY) => this.cdfLutBuffer2D.GetRowSpan(tileY).Slice(tileX * this.luminanceLevels, this.luminanceLevels); - - /// - /// Remaps the grey value with the cdf. - /// - /// The tiles x-position. - /// The tiles y-position. - /// The original luminance. - /// The remapped luminance. - [MethodImpl(InliningOptions.ShortMethod)] - public float RemapGreyValue(int tilesX, int tilesY, int luminance) - { - int cdfMin = this.cdfMinBuffer2D[tilesX, tilesY]; - Span cdfSpan = this.GetCdfLutSpan(tilesX, tilesY); - return (this.pixelsInTile - cdfMin) == 0 - ? cdfSpan[luminance] / this.pixelsInTile - : cdfSpan[luminance] / (float)(this.pixelsInTile - cdfMin); - } - - public void Dispose() - { - this.cdfMinBuffer2D.Dispose(); - this.cdfLutBuffer2D.Dispose(); - } + return new AdaptiveHistEqualizationProcessor(this.LuminanceLevels, this.ClipHistogram, this.ClipLimitPercentage, this.NumberOfTiles); } } } diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor{TPixel}.cs new file mode 100644 index 0000000000..930c4010fa --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor{TPixel}.cs @@ -0,0 +1,546 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; +using System.Collections.Generic; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading.Tasks; + +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Memory; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Normalization +{ + /// + /// Applies an adaptive histogram equalization to the image. The image is split up in tiles. For each tile a cumulative distribution function (cdf) is calculated. + /// To calculate the final equalized pixel value, the cdf value of four adjacent tiles will be interpolated. + /// + /// The pixel format. + internal class AdaptiveHistEqualizationProcessor : HistogramEqualizationProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images + /// or 65536 for 16-bit grayscale images. + /// Indicating whether to clip the histogram bins at a specific value. + /// Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. + /// The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100. + public AdaptiveHistEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage, int tiles) + : base(luminanceLevels, clipHistogram, clipLimitPercentage) + { + Guard.MustBeGreaterThanOrEqualTo(tiles, 2, nameof(tiles)); + Guard.MustBeLessThanOrEqualTo(tiles, 100, nameof(tiles)); + + this.Tiles = tiles; + } + + /// + /// Gets the number of tiles the image is split into (horizontal and vertically) for the adaptive histogram equalization. + /// + private int Tiles { get; } + + /// + protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + int sourceWidth = source.Width; + int sourceHeight = source.Height; + int numberOfPixels = sourceWidth * sourceHeight; + int tileWidth = (int)MathF.Ceiling(sourceWidth / (float)this.Tiles); + int tileHeight = (int)MathF.Ceiling(sourceHeight / (float)this.Tiles); + int pixelsInTile = tileWidth * tileHeight; + int halfTileWidth = tileWidth / 2; + int halfTileHeight = tileHeight / 2; + int luminanceLevels = this.LuminanceLevels; + + // The image is split up into tiles. For each tile the cumulative distribution function will be calculated. + using (var cdfData = new CdfTileData(configuration, sourceWidth, sourceHeight, this.Tiles, this.Tiles, tileWidth, tileHeight, luminanceLevels)) + { + cdfData.CalculateLookupTables(source, this); + + var tileYStartPositions = new List<(int y, int cdfY)>(); + int cdfY = 0; + for (int y = halfTileHeight; y < sourceHeight - halfTileHeight; y += tileHeight) + { + tileYStartPositions.Add((y, cdfY)); + cdfY++; + } + + Parallel.For( + 0, + tileYStartPositions.Count, + new ParallelOptions() { MaxDegreeOfParallelism = configuration.MaxDegreeOfParallelism }, + index => + { + int cdfX = 0; + int tileX = 0; + int tileY = 0; + int y = tileYStartPositions[index].y; + int cdfYY = tileYStartPositions[index].cdfY; + + // It's unfortunate that we have to do this per iteration. + ref TPixel sourceBase = ref source.GetPixelReference(0, 0); + + cdfX = 0; + for (int x = halfTileWidth; x < sourceWidth - halfTileWidth; x += tileWidth) + { + tileY = 0; + int yEnd = Math.Min(y + tileHeight, sourceHeight); + int xEnd = Math.Min(x + tileWidth, sourceWidth); + for (int dy = y; dy < yEnd; dy++) + { + int dyOffSet = dy * sourceWidth; + tileX = 0; + for (int dx = x; dx < xEnd; dx++) + { + ref TPixel pixel = ref Unsafe.Add(ref sourceBase, dyOffSet + dx); + float luminanceEqualized = InterpolateBetweenFourTiles( + pixel, + cdfData, + this.Tiles, + this.Tiles, + tileX, + tileY, + cdfX, + cdfYY, + tileWidth, + tileHeight, + luminanceLevels); + + pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); + tileX++; + } + + tileY++; + } + + cdfX++; + } + }); + + ref TPixel pixelsBase = ref source.GetPixelReference(0, 0); + + // Fix left column + ProcessBorderColumn(ref pixelsBase, cdfData, 0, sourceWidth, sourceHeight, tileWidth, tileHeight, xStart: 0, xEnd: halfTileWidth, luminanceLevels); + + // Fix right column + int rightBorderStartX = ((this.Tiles - 1) * tileWidth) + halfTileWidth; + ProcessBorderColumn(ref pixelsBase, cdfData, this.Tiles - 1, sourceWidth, sourceHeight, tileWidth, tileHeight, xStart: rightBorderStartX, xEnd: sourceWidth, luminanceLevels); + + // Fix top row + ProcessBorderRow(ref pixelsBase, cdfData, 0, sourceWidth, tileWidth, yStart: 0, yEnd: halfTileHeight, luminanceLevels); + + // Fix bottom row + int bottomBorderStartY = ((this.Tiles - 1) * tileHeight) + halfTileHeight; + ProcessBorderRow(ref pixelsBase, cdfData, this.Tiles - 1, sourceWidth, tileWidth, yStart: bottomBorderStartY, yEnd: sourceHeight, luminanceLevels); + + // Left top corner + ProcessCornerTile(ref pixelsBase, cdfData, sourceWidth, 0, 0, xStart: 0, xEnd: halfTileWidth, yStart: 0, yEnd: halfTileHeight, luminanceLevels); + + // Left bottom corner + ProcessCornerTile(ref pixelsBase, cdfData, sourceWidth, 0, this.Tiles - 1, xStart: 0, xEnd: halfTileWidth, yStart: bottomBorderStartY, yEnd: sourceHeight, luminanceLevels); + + // Right top corner + ProcessCornerTile(ref pixelsBase, cdfData, sourceWidth, this.Tiles - 1, 0, xStart: rightBorderStartX, xEnd: sourceWidth, yStart: 0, yEnd: halfTileHeight, luminanceLevels); + + // Right bottom corner + ProcessCornerTile(ref pixelsBase, cdfData, sourceWidth, this.Tiles - 1, this.Tiles - 1, xStart: rightBorderStartX, xEnd: sourceWidth, yStart: bottomBorderStartY, yEnd: sourceHeight, luminanceLevels); + } + } + + /// + /// Processes the part of a corner tile which was previously left out. It consists of 1 / 4 of a tile and does not need interpolation. + /// + /// The output pixels base reference. + /// The lookup table to remap the grey values. + /// The source image width. + /// The x-position in the CDF lookup map. + /// The y-position in the CDF lookup map. + /// X start position. + /// X end position. + /// Y start position. + /// Y end position. + /// + /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images + /// or 65536 for 16-bit grayscale images. + /// + private static void ProcessCornerTile( + ref TPixel pixelsBase, + CdfTileData cdfData, + int sourceWidth, + int cdfX, + int cdfY, + int xStart, + int xEnd, + int yStart, + int yEnd, + int luminanceLevels) + { + for (int dy = yStart; dy < yEnd; dy++) + { + int dyOffSet = dy * sourceWidth; + for (int dx = xStart; dx < xEnd; dx++) + { + ref TPixel pixel = ref Unsafe.Add(ref pixelsBase, dyOffSet + dx); + float luminanceEqualized = cdfData.RemapGreyValue(cdfX, cdfY, GetLuminance(pixel, luminanceLevels)); + pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); + } + } + } + + /// + /// Processes a border column of the image which is half the size of the tile width. + /// + /// The output pixels reference. + /// The pre-computed lookup tables to remap the grey values for each tiles. + /// The X index of the lookup table to use. + /// The source image width. + /// The source image height. + /// The width of a tile. + /// The height of a tile. + /// X start position in the image. + /// X end position of the image. + /// + /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images + /// or 65536 for 16-bit grayscale images. + /// + private static void ProcessBorderColumn( + ref TPixel pixelBase, + CdfTileData cdfData, + int cdfX, + int sourceWidth, + int sourceHeight, + int tileWidth, + int tileHeight, + int xStart, + int xEnd, + int luminanceLevels) + { + int halfTileWidth = tileWidth / 2; + int halfTileHeight = tileHeight / 2; + + int cdfY = 0; + for (int y = halfTileHeight; y < sourceHeight - halfTileHeight; y += tileHeight) + { + int yLimit = Math.Min(y + tileHeight, sourceHeight - 1); + int tileY = 0; + for (int dy = y; dy < yLimit; dy++) + { + int dyOffSet = dy * sourceWidth; + int tileX = halfTileWidth; + for (int dx = xStart; dx < xEnd; dx++) + { + ref TPixel pixel = ref Unsafe.Add(ref pixelBase, dyOffSet + dx); + float luminanceEqualized = InterpolateBetweenTwoTiles(pixel, cdfData, cdfX, cdfY, cdfX, cdfY + 1, tileY, tileHeight, luminanceLevels); + pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); + tileX++; + } + + tileY++; + } + + cdfY++; + } + } + + /// + /// Processes a border row of the image which is half of the size of the tile height. + /// + /// The output pixels base reference. + /// The pre-computed lookup tables to remap the grey values for each tiles. + /// The Y index of the lookup table to use. + /// The source image width. + /// The width of a tile. + /// Y start position in the image. + /// Y end position of the image. + /// + /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images + /// or 65536 for 16-bit grayscale images. + /// + private static void ProcessBorderRow( + ref TPixel pixelBase, + CdfTileData cdfData, + int cdfY, + int sourceWidth, + int tileWidth, + int yStart, + int yEnd, + int luminanceLevels) + { + int halfTileWidth = tileWidth / 2; + + int cdfX = 0; + for (int x = halfTileWidth; x < sourceWidth - halfTileWidth; x += tileWidth) + { + int tileY = 0; + for (int dy = yStart; dy < yEnd; dy++) + { + int dyOffSet = dy * sourceWidth; + int tileX = 0; + int xLimit = Math.Min(x + tileWidth, sourceWidth - 1); + for (int dx = x; dx < xLimit; dx++) + { + ref TPixel pixel = ref Unsafe.Add(ref pixelBase, dyOffSet + dx); + float luminanceEqualized = InterpolateBetweenTwoTiles(pixel, cdfData, cdfX, cdfY, cdfX + 1, cdfY, tileX, tileWidth, luminanceLevels); + pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); + tileX++; + } + + tileY++; + } + + cdfX++; + } + } + + /// + /// Bilinear interpolation between four adjacent tiles. + /// + /// The pixel to remap the grey value from. + /// The pre-computed lookup tables to remap the grey values for each tiles. + /// The number of tiles in the x-direction. + /// The number of tiles in the y-direction. + /// X position inside the tile. + /// Y position inside the tile. + /// X index of the top left lookup table to use. + /// Y index of the top left lookup table to use. + /// Width of one tile in pixels. + /// Height of one tile in pixels. + /// + /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images + /// or 65536 for 16-bit grayscale images. + /// + /// A re-mapped grey value. + [MethodImpl(InliningOptions.ShortMethod)] + private static float InterpolateBetweenFourTiles( + TPixel sourcePixel, + CdfTileData cdfData, + int tileCountX, + int tileCountY, + int tileX, + int tileY, + int cdfX, + int cdfY, + int tileWidth, + int tileHeight, + int luminanceLevels) + { + int luminance = GetLuminance(sourcePixel, luminanceLevels); + float tx = tileX / (float)(tileWidth - 1); + float ty = tileY / (float)(tileHeight - 1); + + int yTop = cdfY; + int yBottom = Math.Min(tileCountY - 1, yTop + 1); + int xLeft = cdfX; + int xRight = Math.Min(tileCountX - 1, xLeft + 1); + + float cdfLeftTopLuminance = cdfData.RemapGreyValue(xLeft, yTop, luminance); + float cdfRightTopLuminance = cdfData.RemapGreyValue(xRight, yTop, luminance); + float cdfLeftBottomLuminance = cdfData.RemapGreyValue(xLeft, yBottom, luminance); + float cdfRightBottomLuminance = cdfData.RemapGreyValue(xRight, yBottom, luminance); + return BilinearInterpolation(tx, ty, cdfLeftTopLuminance, cdfRightTopLuminance, cdfLeftBottomLuminance, cdfRightBottomLuminance); + } + + /// + /// Linear interpolation between two tiles. + /// + /// The pixel to remap the grey value from. + /// The CDF lookup map. + /// X position inside the first tile. + /// Y position inside the first tile. + /// X position inside the second tile. + /// Y position inside the second tile. + /// Position inside the tile. + /// Width of the tile. + /// + /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images + /// or 65536 for 16-bit grayscale images. + /// + /// A re-mapped grey value. + [MethodImpl(InliningOptions.ShortMethod)] + private static float InterpolateBetweenTwoTiles( + TPixel sourcePixel, + CdfTileData cdfData, + int tileX1, + int tileY1, + int tileX2, + int tileY2, + int tilePos, + int tileWidth, + int luminanceLevels) + { + int luminance = GetLuminance(sourcePixel, luminanceLevels); + float tx = tilePos / (float)(tileWidth - 1); + + float cdfLuminance1 = cdfData.RemapGreyValue(tileX1, tileY1, luminance); + float cdfLuminance2 = cdfData.RemapGreyValue(tileX2, tileY2, luminance); + return LinearInterpolation(cdfLuminance1, cdfLuminance2, tx); + } + + /// + /// Bilinear interpolation between four tiles. + /// + /// The interpolation value in x direction in the range of [0, 1]. + /// The interpolation value in y direction in the range of [0, 1]. + /// Luminance from top left tile. + /// Luminance from right top tile. + /// Luminance from left bottom tile. + /// Luminance from right bottom tile. + /// Interpolated Luminance. + [MethodImpl(InliningOptions.ShortMethod)] + private static float BilinearInterpolation(float tx, float ty, float lt, float rt, float lb, float rb) + => LinearInterpolation(LinearInterpolation(lt, rt, tx), LinearInterpolation(lb, rb, tx), ty); + + /// + /// Linear interpolation between two grey values. + /// + /// The left value. + /// The right value. + /// The interpolation value between the two values in the range of [0, 1]. + /// The interpolated value. + [MethodImpl(InliningOptions.ShortMethod)] + private static float LinearInterpolation(float left, float right, float t) + => left + ((right - left) * t); + + /// + /// Contains the results of the cumulative distribution function for all tiles. + /// + private sealed class CdfTileData : IDisposable + { + private readonly Configuration configuration; + private readonly MemoryAllocator memoryAllocator; + + // Used for storing the minimum value for each CDF entry. + private readonly Buffer2D cdfMinBuffer2D; + + // Used for storing the LUT for each CDF entry. + private readonly Buffer2D cdfLutBuffer2D; + private readonly int pixelsInTile; + private readonly int sourceWidth; + private readonly int sourceHeight; + private readonly int tileWidth; + private readonly int tileHeight; + private readonly int luminanceLevels; + private readonly List<(int y, int cdfY)> tileYStartPositions; + + public CdfTileData( + Configuration configuration, + int sourceWidth, + int sourceHeight, + int tileCountX, + int tileCountY, + int tileWidth, + int tileHeight, + int luminanceLevels) + { + this.configuration = configuration; + this.memoryAllocator = configuration.MemoryAllocator; + this.luminanceLevels = luminanceLevels; + this.cdfMinBuffer2D = this.memoryAllocator.Allocate2D(tileCountX, tileCountY); + this.cdfLutBuffer2D = this.memoryAllocator.Allocate2D(tileCountX * luminanceLevels, tileCountY); + this.sourceWidth = sourceWidth; + this.sourceHeight = sourceHeight; + this.tileWidth = tileWidth; + this.tileHeight = tileHeight; + this.pixelsInTile = tileWidth * tileHeight; + + // Calculate the start positions and rent buffers. + this.tileYStartPositions = new List<(int y, int cdfY)>(); + int cdfY = 0; + for (int y = 0; y < sourceHeight; y += tileHeight) + { + this.tileYStartPositions.Add((y, cdfY)); + cdfY++; + } + } + + public void CalculateLookupTables(ImageFrame source, HistogramEqualizationProcessor processor) + { + int sourceWidth = this.sourceWidth; + int sourceHeight = this.sourceHeight; + int tileWidth = this.tileWidth; + int tileHeight = this.tileHeight; + int luminanceLevels = this.luminanceLevels; + MemoryAllocator memoryAllocator = this.memoryAllocator; + + Parallel.For( + 0, + this.tileYStartPositions.Count, + new ParallelOptions() { MaxDegreeOfParallelism = this.configuration.MaxDegreeOfParallelism }, + index => + { + int cdfX = 0; + int cdfY = this.tileYStartPositions[index].cdfY; + int y = this.tileYStartPositions[index].y; + int endY = Math.Min(y + tileHeight, sourceHeight); + ref TPixel sourceBase = ref source.GetPixelReference(0, 0); + ref int cdfMinBase = ref MemoryMarshal.GetReference(this.cdfMinBuffer2D.GetRowSpan(cdfY)); + + using (IMemoryOwner histogramBuffer = this.memoryAllocator.Allocate(luminanceLevels)) + { + Span histogram = histogramBuffer.GetSpan(); + ref int histogramBase = ref MemoryMarshal.GetReference(histogram); + + for (int x = 0; x < sourceWidth; x += tileWidth) + { + histogram.Clear(); + ref int cdfBase = ref MemoryMarshal.GetReference(this.GetCdfLutSpan(cdfX, index)); + + int xlimit = Math.Min(x + tileWidth, sourceWidth); + for (int dy = y; dy < endY; dy++) + { + int dyOffset = dy * sourceWidth; + for (int dx = x; dx < xlimit; dx++) + { + int luminace = GetLuminance(Unsafe.Add(ref sourceBase, dyOffset + dx), luminanceLevels); + histogram[luminace]++; + } + } + + if (processor.ClipHistogramEnabled) + { + processor.ClipHistogram(histogram, processor.ClipLimitPercentage, this.pixelsInTile); + } + + Unsafe.Add(ref cdfMinBase, cdfX) = processor.CalculateCdf(ref cdfBase, ref histogramBase, histogram.Length - 1); + + cdfX++; + } + } + }); + } + + [MethodImpl(InliningOptions.ShortMethod)] + public Span GetCdfLutSpan(int tileX, int tileY) => this.cdfLutBuffer2D.GetRowSpan(tileY).Slice(tileX * this.luminanceLevels, this.luminanceLevels); + + /// + /// Remaps the grey value with the cdf. + /// + /// The tiles x-position. + /// The tiles y-position. + /// The original luminance. + /// The remapped luminance. + [MethodImpl(InliningOptions.ShortMethod)] + public float RemapGreyValue(int tilesX, int tilesY, int luminance) + { + int cdfMin = this.cdfMinBuffer2D[tilesX, tilesY]; + Span cdfSpan = this.GetCdfLutSpan(tilesX, tilesY); + return (this.pixelsInTile - cdfMin) == 0 + ? cdfSpan[luminance] / this.pixelsInTile + : cdfSpan[luminance] / (float)(this.pixelsInTile - cdfMin); + } + + public void Dispose() + { + this.cdfMinBuffer2D.Dispose(); + this.cdfLutBuffer2D.Dispose(); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor.cs index aa9b530c6d..cd4a9644ff 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor.cs @@ -1,389 +1,36 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; -using System.Buffers; -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Threading.Tasks; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; -using SixLabors.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Normalization { /// /// Applies an adaptive histogram equalization to the image using an sliding window approach. /// - /// The pixel format. - internal class AdaptiveHistEqualizationSWProcessor : HistogramEqualizationProcessor - where TPixel : struct, IPixel + internal class AdaptiveHistEqualizationSWProcessor : HistogramEqualizationProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images /// or 65536 for 16-bit grayscale images. /// Indicating whether to clip the histogram bins at a specific value. /// Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. - /// The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100. - public AdaptiveHistEqualizationSWProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage, int tiles) + /// The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100. + public AdaptiveHistEqualizationSWProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage, int numberOfTiles) : base(luminanceLevels, clipHistogram, clipLimitPercentage) { - Guard.MustBeGreaterThanOrEqualTo(tiles, 2, nameof(tiles)); - Guard.MustBeLessThanOrEqualTo(tiles, 100, nameof(tiles)); - - this.Tiles = tiles; + this.NumberOfTiles = numberOfTiles; } /// /// Gets the number of tiles the image is split into (horizontal and vertically) for the adaptive histogram equalization. /// - private int Tiles { get; } - - /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - { - MemoryAllocator memoryAllocator = configuration.MemoryAllocator; - int numberOfPixels = source.Width * source.Height; - Span pixels = source.GetPixelSpan(); - - var parallelOptions = new ParallelOptions() { MaxDegreeOfParallelism = configuration.MaxDegreeOfParallelism }; - int tileWidth = source.Width / this.Tiles; - int tileHeight = tileWidth; - int pixeInTile = tileWidth * tileHeight; - int halfTileHeight = tileHeight / 2; - int halfTileWidth = halfTileHeight; - var slidingWindowInfos = new SlidingWindowInfos(tileWidth, tileHeight, halfTileWidth, halfTileHeight, pixeInTile); - - using (Buffer2D targetPixels = configuration.MemoryAllocator.Allocate2D(source.Width, source.Height)) - { - // Process the inner tiles, which do not require to check the borders. - Parallel.For( - halfTileWidth, - source.Width - halfTileWidth, - parallelOptions, - this.ProcessSlidingWindow( - source, - memoryAllocator, - targetPixels, - slidingWindowInfos, - yStart: halfTileHeight, - yEnd: source.Height - halfTileHeight, - useFastPath: true, - configuration)); - - // Process the left border of the image. - Parallel.For( - 0, - halfTileWidth, - parallelOptions, - this.ProcessSlidingWindow( - source, - memoryAllocator, - targetPixels, - slidingWindowInfos, - yStart: 0, - yEnd: source.Height, - useFastPath: false, - configuration)); - - // Process the right border of the image. - Parallel.For( - source.Width - halfTileWidth, - source.Width, - parallelOptions, - this.ProcessSlidingWindow( - source, - memoryAllocator, - targetPixels, - slidingWindowInfos, - yStart: 0, - yEnd: source.Height, - useFastPath: false, - configuration)); - - // Process the top border of the image. - Parallel.For( - halfTileWidth, - source.Width - halfTileWidth, - parallelOptions, - this.ProcessSlidingWindow( - source, - memoryAllocator, - targetPixels, - slidingWindowInfos, - yStart: 0, - yEnd: halfTileHeight, - useFastPath: false, - configuration)); - - // Process the bottom border of the image. - Parallel.For( - halfTileWidth, - source.Width - halfTileWidth, - parallelOptions, - this.ProcessSlidingWindow( - source, - memoryAllocator, - targetPixels, - slidingWindowInfos, - yStart: source.Height - halfTileHeight, - yEnd: source.Height, - useFastPath: false, - configuration)); - - Buffer2D.SwapOrCopyContent(source.PixelBuffer, targetPixels); - } - } - - /// - /// Applies the sliding window equalization to one column of the image. The window is moved from top to bottom. - /// Moving the window one pixel down requires to remove one row from the top of the window from the histogram and - /// adding a new row at the bottom. - /// - /// The source image. - /// The memory allocator. - /// The target pixels. - /// Informations about the sliding window dimensions. - /// The y start position. - /// The y end position. - /// if set to true the borders of the image will not be checked. - /// The configuration. - /// Action Delegate. - private Action ProcessSlidingWindow( - ImageFrame source, - MemoryAllocator memoryAllocator, - Buffer2D targetPixels, - SlidingWindowInfos swInfos, - int yStart, - int yEnd, - bool useFastPath, - Configuration configuration) - { - return x => - { - using (IMemoryOwner histogramBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean)) - using (IMemoryOwner histogramBufferCopy = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean)) - using (IMemoryOwner cdfBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean)) - using (IMemoryOwner pixelRowBuffer = memoryAllocator.Allocate(swInfos.TileWidth, AllocationOptions.Clean)) - { - Span histogram = histogramBuffer.GetSpan(); - ref int histogramBase = ref MemoryMarshal.GetReference(histogram); - - Span histogramCopy = histogramBufferCopy.GetSpan(); - ref int histogramCopyBase = ref MemoryMarshal.GetReference(histogramCopy); - - ref int cdfBase = ref MemoryMarshal.GetReference(cdfBuffer.GetSpan()); - - Span pixelRow = pixelRowBuffer.GetSpan(); - ref Vector4 pixelRowBase = ref MemoryMarshal.GetReference(pixelRow); - - // Build the initial histogram of grayscale values. - for (int dy = yStart - swInfos.HalfTileHeight; dy < yStart + swInfos.HalfTileHeight; dy++) - { - if (useFastPath) - { - this.CopyPixelRowFast(source, pixelRow, x - swInfos.HalfTileWidth, dy, swInfos.TileWidth, configuration); - } - else - { - this.CopyPixelRow(source, pixelRow, x - swInfos.HalfTileWidth, dy, swInfos.TileWidth, configuration); - } - - this.AddPixelsToHistogram(ref pixelRowBase, ref histogramBase, this.LuminanceLevels, pixelRow.Length); - } - - for (int y = yStart; y < yEnd; y++) - { - if (this.ClipHistogramEnabled) - { - // Clipping the histogram, but doing it on a copy to keep the original un-clipped values for the next iteration. - histogram.CopyTo(histogramCopy); - this.ClipHistogram(histogramCopy, this.ClipLimitPercentage, swInfos.PixeInTile); - } - - // Calculate the cumulative distribution function, which will map each input pixel in the current tile to a new value. - int cdfMin = this.ClipHistogramEnabled - ? this.CalculateCdf(ref cdfBase, ref histogramCopyBase, histogram.Length - 1) - : this.CalculateCdf(ref cdfBase, ref histogramBase, histogram.Length - 1); - - float numberOfPixelsMinusCdfMin = swInfos.PixeInTile - cdfMin; - - // Map the current pixel to the new equalized value. - int luminance = GetLuminance(source[x, y], this.LuminanceLevels); - float luminanceEqualized = Unsafe.Add(ref cdfBase, luminance) / numberOfPixelsMinusCdfMin; - targetPixels[x, y].FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, source[x, y].ToVector4().W)); - - // Remove top most row from the histogram, mirroring rows which exceeds the borders. - if (useFastPath) - { - this.CopyPixelRowFast(source, pixelRow, x - swInfos.HalfTileWidth, y - swInfos.HalfTileWidth, swInfos.TileWidth, configuration); - } - else - { - this.CopyPixelRow(source, pixelRow, x - swInfos.HalfTileWidth, y - swInfos.HalfTileWidth, swInfos.TileWidth, configuration); - } - - this.RemovePixelsFromHistogram(ref pixelRowBase, ref histogramBase, this.LuminanceLevels, pixelRow.Length); - - // Add new bottom row to the histogram, mirroring rows which exceeds the borders. - if (useFastPath) - { - this.CopyPixelRowFast(source, pixelRow, x - swInfos.HalfTileWidth, y + swInfos.HalfTileWidth, swInfos.TileWidth, configuration); - } - else - { - this.CopyPixelRow(source, pixelRow, x - swInfos.HalfTileWidth, y + swInfos.HalfTileWidth, swInfos.TileWidth, configuration); - } - - this.AddPixelsToHistogram(ref pixelRowBase, ref histogramBase, this.LuminanceLevels, pixelRow.Length); - } - } - }; - } - - /// - /// Get the a pixel row at a given position with a length of the tile width. Mirrors pixels which exceeds the edges. - /// - /// The source image. - /// Pre-allocated pixel row span of the size of a the tile width. - /// The x position. - /// The y position. - /// The width in pixels of a tile. - /// The configuration. - private void CopyPixelRow( - ImageFrame source, - Span rowPixels, - int x, - int y, - int tileWidth, - Configuration configuration) - { - if (y < 0) - { - y = ImageMaths.FastAbs(y); - } - else if (y >= source.Height) - { - int diff = y - source.Height; - y = source.Height - diff - 1; - } - - // Special cases for the left and the right border where GetPixelRowSpan can not be used. - if (x < 0) - { - rowPixels.Clear(); - int idx = 0; - for (int dx = x; dx < x + tileWidth; dx++) - { - rowPixels[idx] = source[ImageMaths.FastAbs(dx), y].ToVector4(); - idx++; - } - - return; - } - else if (x + tileWidth > source.Width) - { - rowPixels.Clear(); - int idx = 0; - for (int dx = x; dx < x + tileWidth; dx++) - { - if (dx >= source.Width) - { - int diff = dx - source.Width; - rowPixels[idx] = source[dx - diff - 1, y].ToVector4(); - } - else - { - rowPixels[idx] = source[dx, y].ToVector4(); - } - - idx++; - } + public int NumberOfTiles { get; } - return; - } - - this.CopyPixelRowFast(source, rowPixels, x, y, tileWidth, configuration); - } - - /// - /// Get the a pixel row at a given position with a length of the tile width. - /// - /// The source image. - /// Pre-allocated pixel row span of the size of a the tile width. - /// The x position. - /// The y position. - /// The width in pixels of a tile. - /// The configuration. - [MethodImpl(InliningOptions.ShortMethod)] - private void CopyPixelRowFast( - ImageFrame source, - Span rowPixels, - int x, - int y, - int tileWidth, - Configuration configuration) - => PixelOperations.Instance.ToVector4(configuration, source.GetPixelRowSpan(y).Slice(start: x, length: tileWidth), rowPixels); - - /// - /// Adds a column of grey values to the histogram. - /// - /// The reference to the span of grey values to add. - /// The reference to the histogram span. - /// The number of different luminance levels. - /// The grey values span length. - [MethodImpl(InliningOptions.ShortMethod)] - private void AddPixelsToHistogram(ref Vector4 greyValuesBase, ref int histogramBase, int luminanceLevels, int length) - { - for (int idx = 0; idx < length; idx++) - { - int luminance = GetLuminance(ref Unsafe.Add(ref greyValuesBase, idx), luminanceLevels); - Unsafe.Add(ref histogramBase, luminance)++; - } - } - - /// - /// Removes a column of grey values from the histogram. - /// - /// The reference to the span of grey values to remove. - /// The reference to the histogram span. - /// The number of different luminance levels. - /// The grey values span length. - [MethodImpl(InliningOptions.ShortMethod)] - private void RemovePixelsFromHistogram(ref Vector4 greyValuesBase, ref int histogramBase, int luminanceLevels, int length) - { - for (int idx = 0; idx < length; idx++) - { - int luminance = GetLuminance(ref Unsafe.Add(ref greyValuesBase, idx), luminanceLevels); - Unsafe.Add(ref histogramBase, luminance)--; - } - } - - private class SlidingWindowInfos + /// + public override IImageProcessor CreatePixelSpecificProcessor() { - public SlidingWindowInfos(int tileWidth, int tileHeight, int halfTileWidth, int halfTileHeight, int pixeInTile) - { - this.TileWidth = tileWidth; - this.TileHeight = tileHeight; - this.HalfTileWidth = halfTileWidth; - this.HalfTileHeight = halfTileHeight; - this.PixeInTile = pixeInTile; - } - - public int TileWidth { get; private set; } - - public int TileHeight { get; private set; } - - public int PixeInTile { get; private set; } - - public int HalfTileWidth { get; private set; } - - public int HalfTileHeight { get; private set; } + return new AdaptiveHistEqualizationSWProcessor(this.LuminanceLevels, this.ClipHistogram, this.ClipLimitPercentage, this.NumberOfTiles); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor{TPixel}.cs new file mode 100644 index 0000000000..3584b1a88b --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor{TPixel}.cs @@ -0,0 +1,390 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading.Tasks; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Memory; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Normalization +{ + /// + /// Applies an adaptive histogram equalization to the image using an sliding window approach. + /// + /// The pixel format. + internal class AdaptiveHistEqualizationSWProcessor : HistogramEqualizationProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images + /// or 65536 for 16-bit grayscale images. + /// Indicating whether to clip the histogram bins at a specific value. + /// Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. + /// The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100. + public AdaptiveHistEqualizationSWProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage, int tiles) + : base(luminanceLevels, clipHistogram, clipLimitPercentage) + { + Guard.MustBeGreaterThanOrEqualTo(tiles, 2, nameof(tiles)); + Guard.MustBeLessThanOrEqualTo(tiles, 100, nameof(tiles)); + + this.Tiles = tiles; + } + + /// + /// Gets the number of tiles the image is split into (horizontal and vertically) for the adaptive histogram equalization. + /// + private int Tiles { get; } + + /// + protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + MemoryAllocator memoryAllocator = configuration.MemoryAllocator; + int numberOfPixels = source.Width * source.Height; + Span pixels = source.GetPixelSpan(); + + var parallelOptions = new ParallelOptions() { MaxDegreeOfParallelism = configuration.MaxDegreeOfParallelism }; + int tileWidth = source.Width / this.Tiles; + int tileHeight = tileWidth; + int pixeInTile = tileWidth * tileHeight; + int halfTileHeight = tileHeight / 2; + int halfTileWidth = halfTileHeight; + var slidingWindowInfos = new SlidingWindowInfos(tileWidth, tileHeight, halfTileWidth, halfTileHeight, pixeInTile); + + using (Buffer2D targetPixels = configuration.MemoryAllocator.Allocate2D(source.Width, source.Height)) + { + // Process the inner tiles, which do not require to check the borders. + Parallel.For( + halfTileWidth, + source.Width - halfTileWidth, + parallelOptions, + this.ProcessSlidingWindow( + source, + memoryAllocator, + targetPixels, + slidingWindowInfos, + yStart: halfTileHeight, + yEnd: source.Height - halfTileHeight, + useFastPath: true, + configuration)); + + // Process the left border of the image. + Parallel.For( + 0, + halfTileWidth, + parallelOptions, + this.ProcessSlidingWindow( + source, + memoryAllocator, + targetPixels, + slidingWindowInfos, + yStart: 0, + yEnd: source.Height, + useFastPath: false, + configuration)); + + // Process the right border of the image. + Parallel.For( + source.Width - halfTileWidth, + source.Width, + parallelOptions, + this.ProcessSlidingWindow( + source, + memoryAllocator, + targetPixels, + slidingWindowInfos, + yStart: 0, + yEnd: source.Height, + useFastPath: false, + configuration)); + + // Process the top border of the image. + Parallel.For( + halfTileWidth, + source.Width - halfTileWidth, + parallelOptions, + this.ProcessSlidingWindow( + source, + memoryAllocator, + targetPixels, + slidingWindowInfos, + yStart: 0, + yEnd: halfTileHeight, + useFastPath: false, + configuration)); + + // Process the bottom border of the image. + Parallel.For( + halfTileWidth, + source.Width - halfTileWidth, + parallelOptions, + this.ProcessSlidingWindow( + source, + memoryAllocator, + targetPixels, + slidingWindowInfos, + yStart: source.Height - halfTileHeight, + yEnd: source.Height, + useFastPath: false, + configuration)); + + Buffer2D.SwapOrCopyContent(source.PixelBuffer, targetPixels); + } + } + + /// + /// Applies the sliding window equalization to one column of the image. The window is moved from top to bottom. + /// Moving the window one pixel down requires to remove one row from the top of the window from the histogram and + /// adding a new row at the bottom. + /// + /// The source image. + /// The memory allocator. + /// The target pixels. + /// Informations about the sliding window dimensions. + /// The y start position. + /// The y end position. + /// if set to true the borders of the image will not be checked. + /// The configuration. + /// Action Delegate. + private Action ProcessSlidingWindow( + ImageFrame source, + MemoryAllocator memoryAllocator, + Buffer2D targetPixels, + SlidingWindowInfos swInfos, + int yStart, + int yEnd, + bool useFastPath, + Configuration configuration) + { + return x => + { + using (IMemoryOwner histogramBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean)) + using (IMemoryOwner histogramBufferCopy = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean)) + using (IMemoryOwner cdfBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean)) + using (IMemoryOwner pixelRowBuffer = memoryAllocator.Allocate(swInfos.TileWidth, AllocationOptions.Clean)) + { + Span histogram = histogramBuffer.GetSpan(); + ref int histogramBase = ref MemoryMarshal.GetReference(histogram); + + Span histogramCopy = histogramBufferCopy.GetSpan(); + ref int histogramCopyBase = ref MemoryMarshal.GetReference(histogramCopy); + + ref int cdfBase = ref MemoryMarshal.GetReference(cdfBuffer.GetSpan()); + + Span pixelRow = pixelRowBuffer.GetSpan(); + ref Vector4 pixelRowBase = ref MemoryMarshal.GetReference(pixelRow); + + // Build the initial histogram of grayscale values. + for (int dy = yStart - swInfos.HalfTileHeight; dy < yStart + swInfos.HalfTileHeight; dy++) + { + if (useFastPath) + { + this.CopyPixelRowFast(source, pixelRow, x - swInfos.HalfTileWidth, dy, swInfos.TileWidth, configuration); + } + else + { + this.CopyPixelRow(source, pixelRow, x - swInfos.HalfTileWidth, dy, swInfos.TileWidth, configuration); + } + + this.AddPixelsToHistogram(ref pixelRowBase, ref histogramBase, this.LuminanceLevels, pixelRow.Length); + } + + for (int y = yStart; y < yEnd; y++) + { + if (this.ClipHistogramEnabled) + { + // Clipping the histogram, but doing it on a copy to keep the original un-clipped values for the next iteration. + histogram.CopyTo(histogramCopy); + this.ClipHistogram(histogramCopy, this.ClipLimitPercentage, swInfos.PixeInTile); + } + + // Calculate the cumulative distribution function, which will map each input pixel in the current tile to a new value. + int cdfMin = this.ClipHistogramEnabled + ? this.CalculateCdf(ref cdfBase, ref histogramCopyBase, histogram.Length - 1) + : this.CalculateCdf(ref cdfBase, ref histogramBase, histogram.Length - 1); + + float numberOfPixelsMinusCdfMin = swInfos.PixeInTile - cdfMin; + + // Map the current pixel to the new equalized value. + int luminance = GetLuminance(source[x, y], this.LuminanceLevels); + float luminanceEqualized = Unsafe.Add(ref cdfBase, luminance) / numberOfPixelsMinusCdfMin; + targetPixels[x, y].FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, source[x, y].ToVector4().W)); + + // Remove top most row from the histogram, mirroring rows which exceeds the borders. + if (useFastPath) + { + this.CopyPixelRowFast(source, pixelRow, x - swInfos.HalfTileWidth, y - swInfos.HalfTileWidth, swInfos.TileWidth, configuration); + } + else + { + this.CopyPixelRow(source, pixelRow, x - swInfos.HalfTileWidth, y - swInfos.HalfTileWidth, swInfos.TileWidth, configuration); + } + + this.RemovePixelsFromHistogram(ref pixelRowBase, ref histogramBase, this.LuminanceLevels, pixelRow.Length); + + // Add new bottom row to the histogram, mirroring rows which exceeds the borders. + if (useFastPath) + { + this.CopyPixelRowFast(source, pixelRow, x - swInfos.HalfTileWidth, y + swInfos.HalfTileWidth, swInfos.TileWidth, configuration); + } + else + { + this.CopyPixelRow(source, pixelRow, x - swInfos.HalfTileWidth, y + swInfos.HalfTileWidth, swInfos.TileWidth, configuration); + } + + this.AddPixelsToHistogram(ref pixelRowBase, ref histogramBase, this.LuminanceLevels, pixelRow.Length); + } + } + }; + } + + /// + /// Get the a pixel row at a given position with a length of the tile width. Mirrors pixels which exceeds the edges. + /// + /// The source image. + /// Pre-allocated pixel row span of the size of a the tile width. + /// The x position. + /// The y position. + /// The width in pixels of a tile. + /// The configuration. + private void CopyPixelRow( + ImageFrame source, + Span rowPixels, + int x, + int y, + int tileWidth, + Configuration configuration) + { + if (y < 0) + { + y = ImageMaths.FastAbs(y); + } + else if (y >= source.Height) + { + int diff = y - source.Height; + y = source.Height - diff - 1; + } + + // Special cases for the left and the right border where GetPixelRowSpan can not be used. + if (x < 0) + { + rowPixels.Clear(); + int idx = 0; + for (int dx = x; dx < x + tileWidth; dx++) + { + rowPixels[idx] = source[ImageMaths.FastAbs(dx), y].ToVector4(); + idx++; + } + + return; + } + else if (x + tileWidth > source.Width) + { + rowPixels.Clear(); + int idx = 0; + for (int dx = x; dx < x + tileWidth; dx++) + { + if (dx >= source.Width) + { + int diff = dx - source.Width; + rowPixels[idx] = source[dx - diff - 1, y].ToVector4(); + } + else + { + rowPixels[idx] = source[dx, y].ToVector4(); + } + + idx++; + } + + return; + } + + this.CopyPixelRowFast(source, rowPixels, x, y, tileWidth, configuration); + } + + /// + /// Get the a pixel row at a given position with a length of the tile width. + /// + /// The source image. + /// Pre-allocated pixel row span of the size of a the tile width. + /// The x position. + /// The y position. + /// The width in pixels of a tile. + /// The configuration. + [MethodImpl(InliningOptions.ShortMethod)] + private void CopyPixelRowFast( + ImageFrame source, + Span rowPixels, + int x, + int y, + int tileWidth, + Configuration configuration) + => PixelOperations.Instance.ToVector4(configuration, source.GetPixelRowSpan(y).Slice(start: x, length: tileWidth), rowPixels); + + /// + /// Adds a column of grey values to the histogram. + /// + /// The reference to the span of grey values to add. + /// The reference to the histogram span. + /// The number of different luminance levels. + /// The grey values span length. + [MethodImpl(InliningOptions.ShortMethod)] + private void AddPixelsToHistogram(ref Vector4 greyValuesBase, ref int histogramBase, int luminanceLevels, int length) + { + for (int idx = 0; idx < length; idx++) + { + int luminance = GetLuminance(ref Unsafe.Add(ref greyValuesBase, idx), luminanceLevels); + Unsafe.Add(ref histogramBase, luminance)++; + } + } + + /// + /// Removes a column of grey values from the histogram. + /// + /// The reference to the span of grey values to remove. + /// The reference to the histogram span. + /// The number of different luminance levels. + /// The grey values span length. + [MethodImpl(InliningOptions.ShortMethod)] + private void RemovePixelsFromHistogram(ref Vector4 greyValuesBase, ref int histogramBase, int luminanceLevels, int length) + { + for (int idx = 0; idx < length; idx++) + { + int luminance = GetLuminance(ref Unsafe.Add(ref greyValuesBase, idx), luminanceLevels); + Unsafe.Add(ref histogramBase, luminance)--; + } + } + + private class SlidingWindowInfos + { + public SlidingWindowInfos(int tileWidth, int tileHeight, int halfTileWidth, int halfTileHeight, int pixeInTile) + { + this.TileWidth = tileWidth; + this.TileHeight = tileHeight; + this.HalfTileWidth = halfTileWidth; + this.HalfTileHeight = halfTileHeight; + this.PixeInTile = pixeInTile; + } + + public int TileWidth { get; private set; } + + public int TileHeight { get; private set; } + + public int PixeInTile { get; private set; } + + public int HalfTileWidth { get; private set; } + + public int HalfTileHeight { get; private set; } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs index aadde2424b..62e0185950 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs @@ -1,106 +1,25 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; -using System.Buffers; -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; -using SixLabors.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Normalization { /// - /// Applies a global histogram equalization to the image. + /// Defines a global histogram equalization applicable to an . /// - /// The pixel format. - internal class GlobalHistogramEqualizationProcessor : HistogramEqualizationProcessor - where TPixel : struct, IPixel + internal class GlobalHistogramEqualizationProcessor : HistogramEqualizationProcessor { - /// - /// Initializes a new instance of the class. - /// - /// - /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images - /// or 65536 for 16-bit grayscale images. - /// - /// Indicating whether to clip the histogram bins at a specific value. - /// Histogram clip limit in percent of the total pixels. Histogram bins which exceed this limit, will be capped at this value. public GlobalHistogramEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage) : base(luminanceLevels, clipHistogram, clipLimitPercentage) { } - /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + /// + public override IImageProcessor CreatePixelSpecificProcessor() { - MemoryAllocator memoryAllocator = configuration.MemoryAllocator; - int numberOfPixels = source.Width * source.Height; - Span pixels = source.GetPixelSpan(); - var workingRect = new Rectangle(0, 0, source.Width, source.Height); - - using (IMemoryOwner histogramBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean)) - using (IMemoryOwner cdfBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean)) - { - // Build the histogram of the grayscale levels. - ParallelHelper.IterateRows( - workingRect, - configuration, - rows => - { - ref int histogramBase = ref MemoryMarshal.GetReference(histogramBuffer.GetSpan()); - for (int y = rows.Min; y < rows.Max; y++) - { - ref TPixel pixelBase = ref MemoryMarshal.GetReference(source.GetPixelRowSpan(y)); - - for (int x = 0; x < workingRect.Width; x++) - { - int luminance = GetLuminance(Unsafe.Add(ref pixelBase, x), this.LuminanceLevels); - Unsafe.Add(ref histogramBase, luminance)++; - } - } - }); - - Span histogram = histogramBuffer.GetSpan(); - if (this.ClipHistogramEnabled) - { - this.ClipHistogram(histogram, this.ClipLimitPercentage, numberOfPixels); - } - - // Calculate the cumulative distribution function, which will map each input pixel to a new value. - int cdfMin = this.CalculateCdf( - ref MemoryMarshal.GetReference(cdfBuffer.GetSpan()), - ref MemoryMarshal.GetReference(histogram), - histogram.Length - 1); - - float numberOfPixelsMinusCdfMin = numberOfPixels - cdfMin; - - // Apply the cdf to each pixel of the image - ParallelHelper.IterateRows( - workingRect, - configuration, - rows => - { - ref int cdfBase = ref MemoryMarshal.GetReference(cdfBuffer.GetSpan()); - for (int y = rows.Min; y < rows.Max; y++) - { - ref TPixel pixelBase = ref MemoryMarshal.GetReference(source.GetPixelRowSpan(y)); - - for (int x = 0; x < workingRect.Width; x++) - { - ref TPixel pixel = ref Unsafe.Add(ref pixelBase, x); - int luminance = GetLuminance(pixel, this.LuminanceLevels); - float luminanceEqualized = Unsafe.Add(ref cdfBase, luminance) / numberOfPixelsMinusCdfMin; - pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); - } - } - }); - } + return new GlobalHistogramEqualizationProcessor( + this.LuminanceLevels, + this.ClipHistogram, + this.ClipLimitPercentage); } } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs new file mode 100644 index 0000000000..b3a1603e6f --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs @@ -0,0 +1,107 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Memory; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Normalization +{ + /// + /// Applies a global histogram equalization to the image. + /// + /// The pixel format. + internal class GlobalHistogramEqualizationProcessor : HistogramEqualizationProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + /// + /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images + /// or 65536 for 16-bit grayscale images. + /// + /// Indicating whether to clip the histogram bins at a specific value. + /// Histogram clip limit in percent of the total pixels. Histogram bins which exceed this limit, will be capped at this value. + public GlobalHistogramEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage) + : base(luminanceLevels, clipHistogram, clipLimitPercentage) + { + } + + /// + protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + MemoryAllocator memoryAllocator = configuration.MemoryAllocator; + int numberOfPixels = source.Width * source.Height; + Span pixels = source.GetPixelSpan(); + var workingRect = new Rectangle(0, 0, source.Width, source.Height); + + using (IMemoryOwner histogramBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean)) + using (IMemoryOwner cdfBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean)) + { + // Build the histogram of the grayscale levels. + ParallelHelper.IterateRows( + workingRect, + configuration, + rows => + { + ref int histogramBase = ref MemoryMarshal.GetReference(histogramBuffer.GetSpan()); + for (int y = rows.Min; y < rows.Max; y++) + { + ref TPixel pixelBase = ref MemoryMarshal.GetReference(source.GetPixelRowSpan(y)); + + for (int x = 0; x < workingRect.Width; x++) + { + int luminance = GetLuminance(Unsafe.Add(ref pixelBase, x), this.LuminanceLevels); + Unsafe.Add(ref histogramBase, luminance)++; + } + } + }); + + Span histogram = histogramBuffer.GetSpan(); + if (this.ClipHistogramEnabled) + { + this.ClipHistogram(histogram, this.ClipLimitPercentage, numberOfPixels); + } + + // Calculate the cumulative distribution function, which will map each input pixel to a new value. + int cdfMin = this.CalculateCdf( + ref MemoryMarshal.GetReference(cdfBuffer.GetSpan()), + ref MemoryMarshal.GetReference(histogram), + histogram.Length - 1); + + float numberOfPixelsMinusCdfMin = numberOfPixels - cdfMin; + + // Apply the cdf to each pixel of the image + ParallelHelper.IterateRows( + workingRect, + configuration, + rows => + { + ref int cdfBase = ref MemoryMarshal.GetReference(cdfBuffer.GetSpan()); + for (int y = rows.Min; y < rows.Max; y++) + { + ref TPixel pixelBase = ref MemoryMarshal.GetReference(source.GetPixelRowSpan(y)); + + for (int x = 0; x < workingRect.Width; x++) + { + ref TPixel pixel = ref Unsafe.Add(ref pixelBase, x); + int luminance = GetLuminance(pixel, this.LuminanceLevels); + float luminanceEqualized = Unsafe.Add(ref cdfBase, luminance) / numberOfPixelsMinusCdfMin; + pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); + } + } + }); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationOptions.cs b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationOptions.cs index 0d1a378361..1d9d5c986a 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationOptions.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationOptions.cs @@ -38,6 +38,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// /// Gets or sets the number of tiles the image is split into (horizontal and vertically) for the adaptive histogram equalization. Defaults to 10. /// - public int Tiles { get; set; } = 10; + public int NumberOfTiles { get; set; } = 10; } } diff --git a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs index fd1b6b9784..4aad1f564e 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs @@ -1,10 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Processing.Processors.Normalization @@ -12,14 +8,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// /// Defines a processor that normalizes the histogram of an image. /// - /// The pixel format. - internal abstract class HistogramEqualizationProcessor : ImageProcessor - where TPixel : struct, IPixel + internal abstract class HistogramEqualizationProcessor : IImageProcessor { - private readonly float luminanceLevelsFloat; - /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images /// or 65536 for 16-bit grayscale images. @@ -27,12 +19,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. protected HistogramEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage) { - Guard.MustBeGreaterThan(luminanceLevels, 0, nameof(luminanceLevels)); - Guard.MustBeGreaterThan(clipLimitPercentage, 0F, nameof(clipLimitPercentage)); - this.LuminanceLevels = luminanceLevels; - this.luminanceLevelsFloat = luminanceLevels; - this.ClipHistogramEnabled = clipHistogram; + this.ClipHistogram = clipHistogram; this.ClipLimitPercentage = clipLimitPercentage; } @@ -44,95 +32,61 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// /// Gets a value indicating whether to clip the histogram bins at a specific value. /// - public bool ClipHistogramEnabled { get; } + public bool ClipHistogram { get; } /// /// Gets the histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. /// public float ClipLimitPercentage { get; } - /// - /// Calculates the cumulative distribution function. - /// - /// The reference to the array holding the cdf. - /// The reference to the histogram of the input image. - /// Index of the maximum of the histogram. - /// The first none zero value of the cdf. - public int CalculateCdf(ref int cdfBase, ref int histogramBase, int maxIdx) - { - int histSum = 0; - int cdfMin = 0; - bool cdfMinFound = false; - - for (int i = 0; i <= maxIdx; i++) - { - histSum += Unsafe.Add(ref histogramBase, i); - if (!cdfMinFound && histSum != 0) - { - cdfMin = histSum; - cdfMinFound = true; - } - - // Creating the lookup table: subtracting cdf min, so we do not need to do that inside the for loop. - Unsafe.Add(ref cdfBase, i) = Math.Max(0, histSum - cdfMin); - } - - return cdfMin; - } + /// + public abstract IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel; /// - /// AHE tends to over amplify the contrast in near-constant regions of the image, since the histogram in such regions is highly concentrated. - /// Clipping the histogram is meant to reduce this effect, by cutting of histogram bin's which exceed a certain amount and redistribute - /// the values over the clip limit to all other bins equally. + /// Creates the that implements the algorithm + /// defined by the given . /// - /// The histogram to apply the clipping. - /// Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. - /// The numbers of pixels inside the tile. - public void ClipHistogram(Span histogram, float clipLimitPercentage, int pixelCount) + /// The . + /// The . + public static HistogramEqualizationProcessor FromOptions(HistogramEqualizationOptions options) { - int clipLimit = (int)MathF.Round(pixelCount * clipLimitPercentage); - int sumOverClip = 0; - ref int histogramBase = ref MemoryMarshal.GetReference(histogram); + HistogramEqualizationProcessor processor; - for (int i = 0; i < histogram.Length; i++) + switch (options.Method) { - ref int histogramLevel = ref Unsafe.Add(ref histogramBase, i); - if (histogramLevel > clipLimit) - { - sumOverClip += histogramLevel - clipLimit; - histogramLevel = clipLimit; - } + case HistogramEqualizationMethod.Global: + processor = new GlobalHistogramEqualizationProcessor( + options.LuminanceLevels, + options.ClipHistogram, + options.ClipLimitPercentage); + break; + + case HistogramEqualizationMethod.AdaptiveTileInterpolation: + processor = new AdaptiveHistEqualizationProcessor( + options.LuminanceLevels, + options.ClipHistogram, + options.ClipLimitPercentage, + options.NumberOfTiles); + break; + + case HistogramEqualizationMethod.AdaptiveSlidingWindow: + processor = new AdaptiveHistEqualizationSWProcessor( + options.LuminanceLevels, + options.ClipHistogram, + options.ClipLimitPercentage, + options.NumberOfTiles); + break; + + default: + processor = new GlobalHistogramEqualizationProcessor( + options.LuminanceLevels, + options.ClipHistogram, + options.ClipLimitPercentage); + break; } - int addToEachBin = sumOverClip > 0 ? (int)MathF.Floor(sumOverClip / this.luminanceLevelsFloat) : 0; - if (addToEachBin > 0) - { - for (int i = 0; i < histogram.Length; i++) - { - Unsafe.Add(ref histogramBase, i) += addToEachBin; - } - } + return processor; } - - /// - /// Convert the pixel values to grayscale using ITU-R Recommendation BT.709. - /// - /// The pixel to get the luminance from - /// The number of luminance levels (256 for 8 bit, 65536 for 16 bit grayscale images) - [MethodImpl(InliningOptions.ShortMethod)] - public static int GetLuminance(TPixel sourcePixel, int luminanceLevels) - { - var vector = sourcePixel.ToVector4(); - return GetLuminance(ref vector, luminanceLevels); - } - - /// - /// Convert the pixel values to grayscale using ITU-R Recommendation BT.709. - /// - /// The vector to get the luminance from - /// The number of luminance levels (256 for 8 bit, 65536 for 16 bit grayscale images) - [MethodImpl(InliningOptions.ShortMethod)] - public static int GetLuminance(ref Vector4 vector, int luminanceLevels) - => (int)MathF.Round(((.2126F * vector.X) + (.7152F * vector.Y) + (.0722F * vector.Y)) * (luminanceLevels - 1)); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor{TPixel}.cs new file mode 100644 index 0000000000..8dbc903f29 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor{TPixel}.cs @@ -0,0 +1,139 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors.Normalization +{ + /// + /// Defines a processor that normalizes the histogram of an image. + /// + /// The pixel format. + internal abstract class HistogramEqualizationProcessor : ImageProcessor + where TPixel : struct, IPixel + { + private readonly float luminanceLevelsFloat; + + /// + /// Initializes a new instance of the class. + /// + /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images + /// or 65536 for 16-bit grayscale images. + /// Indicates, if histogram bins should be clipped. + /// Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. + protected HistogramEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage) + { + Guard.MustBeGreaterThan(luminanceLevels, 0, nameof(luminanceLevels)); + Guard.MustBeGreaterThan(clipLimitPercentage, 0F, nameof(clipLimitPercentage)); + + this.LuminanceLevels = luminanceLevels; + this.luminanceLevelsFloat = luminanceLevels; + this.ClipHistogramEnabled = clipHistogram; + this.ClipLimitPercentage = clipLimitPercentage; + } + + /// + /// Gets the number of luminance levels. + /// + public int LuminanceLevels { get; } + + /// + /// Gets a value indicating whether to clip the histogram bins at a specific value. + /// + public bool ClipHistogramEnabled { get; } + + /// + /// Gets the histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. + /// + public float ClipLimitPercentage { get; } + + /// + /// Calculates the cumulative distribution function. + /// + /// The reference to the array holding the cdf. + /// The reference to the histogram of the input image. + /// Index of the maximum of the histogram. + /// The first none zero value of the cdf. + public int CalculateCdf(ref int cdfBase, ref int histogramBase, int maxIdx) + { + int histSum = 0; + int cdfMin = 0; + bool cdfMinFound = false; + + for (int i = 0; i <= maxIdx; i++) + { + histSum += Unsafe.Add(ref histogramBase, i); + if (!cdfMinFound && histSum != 0) + { + cdfMin = histSum; + cdfMinFound = true; + } + + // Creating the lookup table: subtracting cdf min, so we do not need to do that inside the for loop. + Unsafe.Add(ref cdfBase, i) = Math.Max(0, histSum - cdfMin); + } + + return cdfMin; + } + + /// + /// AHE tends to over amplify the contrast in near-constant regions of the image, since the histogram in such regions is highly concentrated. + /// Clipping the histogram is meant to reduce this effect, by cutting of histogram bin's which exceed a certain amount and redistribute + /// the values over the clip limit to all other bins equally. + /// + /// The histogram to apply the clipping. + /// Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. + /// The numbers of pixels inside the tile. + public void ClipHistogram(Span histogram, float clipLimitPercentage, int pixelCount) + { + int clipLimit = (int)MathF.Round(pixelCount * clipLimitPercentage); + int sumOverClip = 0; + ref int histogramBase = ref MemoryMarshal.GetReference(histogram); + + for (int i = 0; i < histogram.Length; i++) + { + ref int histogramLevel = ref Unsafe.Add(ref histogramBase, i); + if (histogramLevel > clipLimit) + { + sumOverClip += histogramLevel - clipLimit; + histogramLevel = clipLimit; + } + } + + int addToEachBin = sumOverClip > 0 ? (int)MathF.Floor(sumOverClip / this.luminanceLevelsFloat) : 0; + if (addToEachBin > 0) + { + for (int i = 0; i < histogram.Length; i++) + { + Unsafe.Add(ref histogramBase, i) += addToEachBin; + } + } + } + + /// + /// Convert the pixel values to grayscale using ITU-R Recommendation BT.709. + /// + /// The pixel to get the luminance from + /// The number of luminance levels (256 for 8 bit, 65536 for 16 bit grayscale images) + [MethodImpl(InliningOptions.ShortMethod)] + public static int GetLuminance(TPixel sourcePixel, int luminanceLevels) + { + var vector = sourcePixel.ToVector4(); + return GetLuminance(ref vector, luminanceLevels); + } + + /// + /// Convert the pixel values to grayscale using ITU-R Recommendation BT.709. + /// + /// The vector to get the luminance from + /// The number of luminance levels (256 for 8 bit, 65536 for 16 bit grayscale images) + [MethodImpl(InliningOptions.ShortMethod)] + public static int GetLuminance(ref Vector4 vector, int luminanceLevels) + => (int)MathF.Round(((.2126F * vector.X) + (.7152F * vector.Y) + (.0722F * vector.Y)) * (luminanceLevels - 1)); + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs b/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs index 84d592bd96..fc9a583dd1 100644 --- a/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs +++ b/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs @@ -86,7 +86,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Normalization Method = HistogramEqualizationMethod.AdaptiveSlidingWindow, LuminanceLevels = 256, ClipHistogram = true, - Tiles = 15 + NumberOfTiles = 15 }; image.Mutate(x => x.HistogramEqualization(options)); image.DebugSave(provider); @@ -106,7 +106,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Normalization Method = HistogramEqualizationMethod.AdaptiveTileInterpolation, LuminanceLevels = 256, ClipHistogram = true, - Tiles = 10 + NumberOfTiles = 10 }; image.Mutate(x => x.HistogramEqualization(options)); image.DebugSave(provider); diff --git a/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs index cb901c2a88..91422faaa1 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs @@ -50,8 +50,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization /// The output is visually correct old 32bit runtime, /// but it is very different because of floating point inaccuracies. /// - private static readonly bool SkipAllDitherTests = - !TestEnvironment.Is64BitProcess && string.IsNullOrEmpty(TestEnvironment.NetCoreVersion); + private static readonly bool SkipAllDitherTests = false; [Theory] [WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Rgba32)] From fa1e0baced645949989929db33a59683855c509e Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 5 May 2019 17:43:06 +0200 Subject: [PATCH 133/223] cache Image dimensions into a field + re-enable all target frameworks --- .../ImageSharp.Drawing.csproj | 4 +- src/ImageSharp/Image.cs | 39 +++++++++++++--- src/ImageSharp/ImageSharp.csproj | 3 +- src/ImageSharp/Image{TPixel}.cs | 45 +++++++++++++------ 4 files changed, 68 insertions(+), 23 deletions(-) diff --git a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj index 1734c48de2..ac5d7fdea6 100644 --- a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj +++ b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj @@ -10,8 +10,8 @@ $(packageversion) 0.0.1 - - netcoreapp2.1 + netcoreapp2.1;netstandard1.3;netstandard2.0 + netstandard2.0 7.3 true diff --git a/src/ImageSharp/Image.cs b/src/ImageSharp/Image.cs index d0a1c4319d..1566fd0eed 100644 --- a/src/ImageSharp/Image.cs +++ b/src/ImageSharp/Image.cs @@ -7,6 +7,7 @@ using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; namespace SixLabors.ImageSharp { @@ -17,19 +18,36 @@ namespace SixLabors.ImageSharp /// public abstract partial class Image : IImage, IConfigurable { + private Size size; + /// /// Initializes a new instance of the class. /// /// The . /// The . /// The . - protected Image(Configuration configuration, PixelTypeInfo pixelType, ImageMetadata metadata) + /// The . + protected Image(Configuration configuration, PixelTypeInfo pixelType, ImageMetadata metadata, Size size) { this.Configuration = configuration ?? Configuration.Default; this.PixelType = pixelType; + this.size = size; this.Metadata = metadata ?? new ImageMetadata(); } + /// + /// Initializes a new instance of the class. + /// + internal Image( + Configuration configuration, + PixelTypeInfo pixelType, + ImageMetadata metadata, + int width, + int height) + : this(configuration, pixelType, metadata, new Size(width, height)) + { + } + /// /// Gets the . /// @@ -39,10 +57,10 @@ namespace SixLabors.ImageSharp public PixelTypeInfo PixelType { get; } /// - public abstract int Width { get; } + public int Width => this.size.Width; /// - public abstract int Height { get; } + public int Height => this.size.Height; /// public ImageMetadata Metadata { get; } @@ -55,8 +73,6 @@ namespace SixLabors.ImageSharp /// public abstract void Dispose(); - internal abstract void AcceptVisitor(IImageVisitor visitor); - /// /// Saves the image to the given stream using the given image encoder. /// @@ -72,6 +88,19 @@ namespace SixLabors.ImageSharp this.AcceptVisitor(visitor); } + /// + /// Accept a . + /// Implemented by invoking + /// with the pixel type of the image. + /// + internal abstract void AcceptVisitor(IImageVisitor visitor); + + /// + /// Update the size of the image after mutation. + /// + /// The . + protected void UpdateSize(Size size) => this.size = size; + private class EncodeVisitor : IImageVisitor { private readonly IImageEncoder encoder; diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index f780ca666a..e763b8ef1c 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -11,8 +11,7 @@ $(packageversion) 0.0.1 - - netcoreapp2.1 + netcoreapp2.1;netstandard1.3;netstandard2.0;net472 true true diff --git a/src/ImageSharp/Image{TPixel}.cs b/src/ImageSharp/Image{TPixel}.cs index 243e2912be..27e2bc593c 100644 --- a/src/ImageSharp/Image{TPixel}.cs +++ b/src/ImageSharp/Image{TPixel}.cs @@ -3,14 +3,13 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; -using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; namespace SixLabors.ImageSharp { @@ -67,14 +66,14 @@ namespace SixLabors.ImageSharp /// The height of the image in pixels. /// The images metadata. internal Image(Configuration configuration, int width, int height, ImageMetadata metadata) - : base(configuration, PixelTypeInfo.Create(), metadata) + : base(configuration, PixelTypeInfo.Create(), metadata, width, height) { this.Frames = new ImageFrameCollection(this, width, height, default(TPixel)); } /// /// Initializes a new instance of the class - /// wrapping an external + /// wrapping an external . /// /// The configuration providing initialization code which allows extending the library. /// The memory source. @@ -82,7 +81,7 @@ namespace SixLabors.ImageSharp /// The height of the image in pixels. /// The images metadata. internal Image(Configuration configuration, MemorySource memorySource, int width, int height, ImageMetadata metadata) - : base(configuration, PixelTypeInfo.Create(), metadata) + : base(configuration, PixelTypeInfo.Create(), metadata, width, height) { this.Frames = new ImageFrameCollection(this, width, height, memorySource); } @@ -97,7 +96,7 @@ namespace SixLabors.ImageSharp /// The color to initialize the pixels with. /// The images metadata. internal Image(Configuration configuration, int width, int height, TPixel backgroundColor, ImageMetadata metadata) - : base(configuration, PixelTypeInfo.Create(), metadata) + : base(configuration, PixelTypeInfo.Create(), metadata, width, height) { this.Frames = new ImageFrameCollection(this, width, height, backgroundColor); } @@ -110,17 +109,11 @@ namespace SixLabors.ImageSharp /// The images metadata. /// The frames that will be owned by this image instance. internal Image(Configuration configuration, ImageMetadata metadata, IEnumerable> frames) - : base(configuration, PixelTypeInfo.Create(), metadata) + : base(configuration, PixelTypeInfo.Create(), metadata, ValidateFramesAndGetSize(frames)) { this.Frames = new ImageFrameCollection(this, frames); } - /// - public override int Width => this.Frames.RootFrame.Width; - - /// - public override int Height => this.Frames.RootFrame.Height; - /// /// Gets the frames. /// @@ -173,7 +166,7 @@ namespace SixLabors.ImageSharp /// /// The pixel format. /// The configuration providing initialization code which allows extending the library. - /// The + /// The . public Image CloneAs(Configuration configuration) where TPixel2 : struct, IPixel { @@ -184,6 +177,7 @@ namespace SixLabors.ImageSharp /// public override void Dispose() => this.Frames.Dispose(); + /// internal override void AcceptVisitor(IImageVisitor visitor) { visitor.Visit(this); @@ -204,6 +198,29 @@ namespace SixLabors.ImageSharp { this.Frames[i].SwapOrCopyPixelsBufferFrom(pixelSource.Frames[i]); } + + this.UpdateSize(pixelSource.Size()); + } + + private static Size ValidateFramesAndGetSize(IEnumerable> frames) + { + Guard.NotNull(frames, nameof(frames)); + + ImageFrame rootFrame = frames.FirstOrDefault(); + + if (rootFrame == null) + { + throw new ArgumentException("Must not be empty.", nameof(frames)); + } + + Size rootSize = rootFrame.Size(); + + if (frames.Any(f => f.Size() != rootSize)) + { + throw new ArgumentException("The provided frames must be of the same size.", nameof(frames)); + } + + return rootSize; } } } \ No newline at end of file From 5215a13cbba354ed0fe1e7fdf5e92e81a1d9912a Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 5 May 2019 19:33:34 +0200 Subject: [PATCH 134/223] fix Image.FromStream() + add tests --- src/ImageSharp/Formats/Bmp/ImageExtensions.cs | 19 +- src/ImageSharp/Formats/Gif/ImageExtensions.cs | 19 +- .../Formats/Jpeg/ImageExtensions.cs | 19 +- src/ImageSharp/Formats/Png/ImageExtensions.cs | 18 +- src/ImageSharp/Image.FromBytes.cs | 207 +++++++++++++----- src/ImageSharp/Image.FromFile.cs | 16 +- src/ImageSharp/Image.FromStream.cs | 50 +++-- .../Drawing/RecolorImageTest.cs | 4 +- .../Drawing/SolidPolygonTests.cs | 2 +- .../Formats/Bmp/BmpEncoderTests.cs | 4 +- .../Formats/GeneralFormatTests.cs | 6 +- .../Formats/Gif/GifDecoderTests.cs | 6 +- .../Formats/Gif/GifEncoderTests.cs | 6 +- .../Formats/Jpg/JpegDecoderTests.MetaData.cs | 2 +- .../Formats/Jpg/JpegEncoderTests.cs | 8 +- .../Formats/Png/PngDecoderTests.cs | 6 +- .../Formats/Png/PngEncoderTests.cs | 6 +- .../Image/ImageTests.ImageLoadTestBase.cs | 25 ++- .../Image/ImageTests.Load_FileSystemPath.cs | 2 +- .../Image/ImageTests.Load_FromBytes.cs | 120 ---------- ...s.Load_FromBytes_PassLocalConfiguration.cs | 119 ++++++++++ ...s.Load_FromBytes_UseGlobalConfiguration.cs | 101 +++++++++ .../Image/ImageTests.Load_FromStream.cs | 102 --------- ....Load_FromStream_PassLocalConfiguration.cs | 93 ++++++++ ...Load_FromStream_UseDefaultConfiguration.cs | 95 ++++++++ .../Profiles/Exif/ExifProfileTests.cs | 12 +- .../MetaData/Profiles/Exif/ExifValueTests.cs | 2 +- .../PorterDuffCompositorTests.cs | 2 +- tests/ImageSharp.Tests/TestFile.cs | 6 +- tests/ImageSharp.Tests/TestFileSystem.cs | 31 +-- tests/ImageSharp.Tests/TestFormat.cs | 63 +++++- .../Tests/ReferenceDecoderBenchmarks.cs | 2 +- 32 files changed, 761 insertions(+), 412 deletions(-) delete mode 100644 tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes.cs create mode 100644 tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_PassLocalConfiguration.cs create mode 100644 tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_UseGlobalConfiguration.cs delete mode 100644 tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream.cs create mode 100644 tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_PassLocalConfiguration.cs create mode 100644 tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_UseDefaultConfiguration.cs diff --git a/src/ImageSharp/Formats/Bmp/ImageExtensions.cs b/src/ImageSharp/Formats/Bmp/ImageExtensions.cs index aa1c353db2..0c37907c22 100644 --- a/src/ImageSharp/Formats/Bmp/ImageExtensions.cs +++ b/src/ImageSharp/Formats/Bmp/ImageExtensions.cs @@ -2,38 +2,35 @@ // Licensed under the Apache License, Version 2.0. using System.IO; + using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats.Bmp; -using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp { /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Saves the image to the given stream with the bmp format. /// - /// The pixel format. /// The image this method extends. /// The stream to save the image to. /// Thrown if the stream is null. - public static void SaveAsBmp(this Image source, Stream stream) - where TPixel : struct, IPixel - => source.SaveAsBmp(stream, null); + public static void SaveAsBmp(this Image source, Stream stream) => source.SaveAsBmp(stream, null); /// /// Saves the image to the given stream with the bmp format. /// - /// The pixel format. /// The image this method extends. /// The stream to save the image to. /// The encoder to save the image with. /// Thrown if the stream is null. - public static void SaveAsBmp(this Image source, Stream stream, BmpEncoder encoder) - where TPixel : struct, IPixel - => source.Save(stream, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(BmpFormat.Instance)); + public static void SaveAsBmp(this Image source, Stream stream, BmpEncoder encoder) => + source.Save( + stream, + encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(BmpFormat.Instance)); } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Gif/ImageExtensions.cs b/src/ImageSharp/Formats/Gif/ImageExtensions.cs index 8ddd4247e1..c7ac001ff5 100644 --- a/src/ImageSharp/Formats/Gif/ImageExtensions.cs +++ b/src/ImageSharp/Formats/Gif/ImageExtensions.cs @@ -2,38 +2,35 @@ // Licensed under the Apache License, Version 2.0. using System.IO; + using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats.Gif; -using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp { /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Saves the image to the given stream in the gif format. /// - /// The pixel format. /// The image this method extends. /// The stream to save the image to. /// Thrown if the stream is null. - public static void SaveAsGif(this Image source, Stream stream) - where TPixel : struct, IPixel - => source.SaveAsGif(stream, null); + public static void SaveAsGif(this Image source, Stream stream) => source.SaveAsGif(stream, null); /// /// Saves the image to the given stream in the gif format. /// - /// The pixel format. /// The image this method extends. /// The stream to save the image to. /// The options for the encoder. /// Thrown if the stream is null. - public static void SaveAsGif(this Image source, Stream stream, GifEncoder encoder) - where TPixel : struct, IPixel - => source.Save(stream, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(GifFormat.Instance)); + public static void SaveAsGif(this Image source, Stream stream, GifEncoder encoder) => + source.Save( + stream, + encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(GifFormat.Instance)); } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/ImageExtensions.cs b/src/ImageSharp/Formats/Jpeg/ImageExtensions.cs index cb7fc19446..7fec050b4a 100644 --- a/src/ImageSharp/Formats/Jpeg/ImageExtensions.cs +++ b/src/ImageSharp/Formats/Jpeg/ImageExtensions.cs @@ -2,38 +2,35 @@ // Licensed under the Apache License, Version 2.0. using System.IO; + using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats.Jpeg; -using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp { /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Saves the image to the given stream with the jpeg format. /// - /// The pixel format. /// The image this method extends. /// The stream to save the image to. /// Thrown if the stream is null. - public static void SaveAsJpeg(this Image source, Stream stream) - where TPixel : struct, IPixel - => SaveAsJpeg(source, stream, null); + public static void SaveAsJpeg(this Image source, Stream stream) => SaveAsJpeg(source, stream, null); /// /// Saves the image to the given stream with the jpeg format. /// - /// The pixel format. /// The image this method extends. /// The stream to save the image to. /// The options for the encoder. /// Thrown if the stream is null. - public static void SaveAsJpeg(this Image source, Stream stream, JpegEncoder encoder) - where TPixel : struct, IPixel - => source.Save(stream, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(JpegFormat.Instance)); + public static void SaveAsJpeg(this Image source, Stream stream, JpegEncoder encoder) => + source.Save( + stream, + encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(JpegFormat.Instance)); } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Png/ImageExtensions.cs b/src/ImageSharp/Formats/Png/ImageExtensions.cs index c73ec6f57e..af56830f42 100644 --- a/src/ImageSharp/Formats/Png/ImageExtensions.cs +++ b/src/ImageSharp/Formats/Png/ImageExtensions.cs @@ -2,39 +2,35 @@ // Licensed under the Apache License, Version 2.0. using System.IO; + using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Png; -using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp { /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Saves the image to the given stream with the png format. /// - /// The pixel format. /// The image this method extends. /// The stream to save the image to. /// Thrown if the stream is null. - public static void SaveAsPng(this Image source, Stream stream) - where TPixel : struct, IPixel - => SaveAsPng(source, stream, null); + public static void SaveAsPng(this Image source, Stream stream) => SaveAsPng(source, stream, null); /// /// Saves the image to the given stream with the png format. /// - /// The pixel format. /// The image this method extends. /// The stream to save the image to. /// The options for the encoder. /// Thrown if the stream is null. - public static void SaveAsPng(this Image source, Stream stream, PngEncoder encoder) - where TPixel : struct, IPixel - => source.Save(stream, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PngFormat.Instance)); + public static void SaveAsPng(this Image source, Stream stream, PngEncoder encoder) => + source.Save( + stream, + encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PngFormat.Instance)); } } \ No newline at end of file diff --git a/src/ImageSharp/Image.FromBytes.cs b/src/ImageSharp/Image.FromBytes.cs index 24a169f6e9..25dc5a1e0e 100644 --- a/src/ImageSharp/Image.FromBytes.cs +++ b/src/ImageSharp/Image.FromBytes.cs @@ -44,48 +44,6 @@ namespace SixLabors.ImageSharp /// A new . public static Image Load(byte[] data) => Load(Configuration.Default, data); - /// - /// Load a new instance of from the given encoded byte array. - /// - /// The byte array containing encoded image data. - /// The mime type of the decoded image. - /// A new . - public static Image Load(byte[] data, out IImageFormat format) => Load(Configuration.Default, data, out format); - - /// - /// Load a new instance of from the given encoded byte array. - /// - /// The config for the decoder. - /// The byte array containing encoded image data. - /// A new . - public static Image Load(Configuration config, byte[] data) => Load(config, data); - - /// - /// Load a new instance of from the given encoded byte array. - /// - /// The config for the decoder. - /// The byte array containing image data. - /// The mime type of the decoded image. - /// A new . - public static Image Load(Configuration config, byte[] data, out IImageFormat format) => Load(config, data, out format); - - /// - /// Load a new instance of from the given encoded byte array. - /// - /// The byte array containing encoded image data. - /// The decoder. - /// A new . - public static Image Load(byte[] data, IImageDecoder decoder) => Load(data, decoder); - - /// - /// Load a new instance of from the given encoded byte array. - /// - /// The config for the decoder. - /// The byte array containing image data. - /// The decoder. - /// A new . - public static Image Load(Configuration config, byte[] data, IImageDecoder decoder) => Load(config, data, decoder); - /// /// Load a new instance of from the given encoded byte array. /// @@ -117,9 +75,9 @@ namespace SixLabors.ImageSharp public static Image Load(Configuration config, byte[] data) where TPixel : struct, IPixel { - using (var steram = new MemoryStream(data)) + using (var stream = new MemoryStream(data)) { - return Load(config, steram); + return Load(config, stream); } } @@ -211,29 +169,36 @@ namespace SixLabors.ImageSharp } /// - /// Load a new instance of from the given encoded byte span. + /// Load a new instance of from the given encoded byte span. /// - /// The byte span containing image data. - /// A new . - public static Image Load(ReadOnlySpan data) => Load(Configuration.Default, data); + /// The byte span containing encoded image data. + /// The pixel format. + /// A new . + public static Image Load(ReadOnlySpan data) + where TPixel : struct, IPixel + => Load(Configuration.Default, data); /// - /// Load a new instance of from the given encoded byte span. + /// Load a new instance of from the given encoded byte array. /// - /// The config for the decoder. - /// The byte span containing encoded image data. - /// A new . - public static Image Load(Configuration config, ReadOnlySpan data) => Load(config, data); + /// The byte span containing image data. + /// The mime type of the decoded image. + /// The pixel format. + /// A new . + public static Image Load(ReadOnlySpan data, out IImageFormat format) + where TPixel : struct, IPixel + => Load(Configuration.Default, data, out format); /// - /// Load a new instance of from the given encoded byte span. + /// Load a new instance of from the given encoded byte array. /// /// The byte span containing encoded image data. + /// The decoder. /// The pixel format. /// A new . - public static Image Load(ReadOnlySpan data) + public static Image Load(ReadOnlySpan data, IImageDecoder decoder) where TPixel : struct, IPixel - => Load(Configuration.Default, data); + => Load(Configuration.Default, data, decoder); /// /// Load a new instance of from the given encoded byte span. @@ -299,5 +264,135 @@ namespace SixLabors.ImageSharp } } } + + /// + /// Load a new instance of from the given encoded byte array. + /// + /// The byte array containing image data. + /// The detected format. + /// A new . + public static Image Load(byte[] data, out IImageFormat format) => + Load(Configuration.Default, data, out format); + + /// + /// Load a new instance of from the given encoded byte array. + /// + /// The byte array containing encoded image data. + /// The decoder. + /// A new . + public static Image Load(byte[] data, IImageDecoder decoder) => Load(Configuration.Default, data, decoder); + + /// + /// Load a new instance of from the given encoded byte array. + /// + /// The config for the decoder. + /// The byte array containing encoded image data. + /// A new . + public static Image Load(Configuration config, byte[] data) => Load(config, data, out _); + + /// + /// Load a new instance of from the given encoded byte array. + /// + /// The config for the decoder. + /// The byte array containing image data. + /// The decoder. + /// A new . + public static Image Load(Configuration config, byte[] data, IImageDecoder decoder) + { + using (var stream = new MemoryStream(data)) + { + return Load(config, stream, decoder); + } + } + + /// + /// Load a new instance of from the given encoded byte array. + /// + /// The config for the decoder. + /// The byte array containing image data. + /// The mime type of the decoded image. + /// A new . + public static Image Load(Configuration config, byte[] data, out IImageFormat format) + { + using (var stream = new MemoryStream(data)) + { + return Load(config, stream, out format); + } + } + + /// + /// Load a new instance of from the given encoded byte span. + /// + /// The byte span containing image data. + /// A new . + public static Image Load(ReadOnlySpan data) => Load(Configuration.Default, data); + + /// + /// Load a new instance of from the given encoded byte span. + /// + /// The byte span containing image data. + /// The decoder. + /// A new . + public static Image Load(ReadOnlySpan data, IImageDecoder decoder) => + Load(Configuration.Default, data, decoder); + + /// + /// Load a new instance of from the given encoded byte array. + /// + /// The byte span containing image data. + /// The detected format. + /// A new . + public static Image Load(ReadOnlySpan data, out IImageFormat format) => + Load(Configuration.Default, data, out format); + + /// + /// Decodes a new instance of from the given encoded byte span. + /// + /// The configuration options. + /// The byte span containing image data. + /// A new . + public static unsafe Image Load(Configuration config, ReadOnlySpan data) => Load(config, data, out _); + + /// + /// Load a new instance of from the given encoded byte span. + /// + /// The Configuration. + /// The byte span containing image data. + /// The decoder. + /// A new . + public static unsafe Image Load( + Configuration config, + ReadOnlySpan data, + IImageDecoder decoder) + { + fixed (byte* ptr = &data.GetPinnableReference()) + { + using (var stream = new UnmanagedMemoryStream(ptr, data.Length)) + { + return Load(config, stream, decoder); + } + } + } + + /// + /// Load a new instance of from the given encoded byte span. + /// + /// The configuration options. + /// The byte span containing image data. + /// The of the decoded image.> + /// A new . + public static unsafe Image Load( + Configuration config, + ReadOnlySpan data, + out IImageFormat format) + { + fixed (byte* ptr = &data.GetPinnableReference()) + { + using (var stream = new UnmanagedMemoryStream(ptr, data.Length)) + { + return Load(config, stream, out format); + } + } + } } } \ No newline at end of file diff --git a/src/ImageSharp/Image.FromFile.cs b/src/ImageSharp/Image.FromFile.cs index 61500e20d7..08ed381c5a 100644 --- a/src/ImageSharp/Image.FromFile.cs +++ b/src/ImageSharp/Image.FromFile.cs @@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp /// Thrown if the stream is not readable nor seekable. /// /// A new . - public static Image Load(string path) => Load(path); + public static Image Load(string path) => Load(Configuration.Default, path); /// /// Create a new instance of the class from the given file. @@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp /// Thrown if the stream is not readable nor seekable. /// /// A new . - public static Image Load(string path, out IImageFormat format) => Load(path, out format); + public static Image Load(string path, out IImageFormat format) => Load(Configuration.Default, path, out format); /// /// Create a new instance of the class from the given file. @@ -68,7 +68,7 @@ namespace SixLabors.ImageSharp /// Thrown if the stream is not readable nor seekable. /// /// A new . - public static Image Load(Configuration config, string path) => Load(config, path); + public static Image Load(Configuration config, string path) => Load(config, path, out _); /// /// Create a new instance of the class from the given file. @@ -80,7 +80,13 @@ namespace SixLabors.ImageSharp /// Thrown if the stream is not readable nor seekable. /// /// A new . - public static Image Load(Configuration config, string path, IImageDecoder decoder) => Load(config, path, decoder); + public static Image Load(Configuration config, string path, IImageDecoder decoder) + { + using (Stream stream = config.FileSystem.OpenRead(path)) + { + return Load(config, stream, decoder); + } + } /// /// Create a new instance of the class from the given file. @@ -91,7 +97,7 @@ namespace SixLabors.ImageSharp /// Thrown if the stream is not readable nor seekable. /// /// A new . - public static Image Load(string path, IImageDecoder decoder) => Load(path, decoder); + public static Image Load(string path, IImageDecoder decoder) => Load(Configuration.Default, path, decoder); /// /// Create a new instance of the class from the given file. diff --git a/src/ImageSharp/Image.FromStream.cs b/src/ImageSharp/Image.FromStream.cs index a4a9eeec75..74b99dfb3f 100644 --- a/src/ImageSharp/Image.FromStream.cs +++ b/src/ImageSharp/Image.FromStream.cs @@ -56,39 +56,54 @@ namespace SixLabors.ImageSharp => WithSeekableStream(config, stream, s => InternalIdentity(s, config ?? Configuration.Default)); /// - /// Create a new instance of the class from the given stream. + /// Decode a new instance of the class from the given stream. + /// The pixel format is selected by the decoder. /// /// The stream containing image information. /// the mime type of the decoded image. /// Thrown if the stream is not readable. - /// A new .> - public static Image Load(Stream stream, out IImageFormat format) => Load(stream, out format); + /// A new .> + public static Image Load(Stream stream, out IImageFormat format) => Load(Configuration.Default, stream, out format); /// - /// Create a new instance of the class from the given stream. + /// Decode a new instance of the class from the given stream. + /// The pixel format is selected by the decoder. /// /// The stream containing image information. /// Thrown if the stream is not readable. - /// A new .> - public static Image Load(Stream stream) => Load(stream); + /// A new .> + public static Image Load(Stream stream) => Load(Configuration.Default, stream); /// - /// Create a new instance of the class from the given stream. + /// Decode a new instance of the class from the given stream. + /// The pixel format is selected by the decoder. /// /// The stream containing image information. /// The decoder. /// Thrown if the stream is not readable. - /// A new .> - public static Image Load(Stream stream, IImageDecoder decoder) => Load(stream, decoder); + /// A new .> + public static Image Load(Stream stream, IImageDecoder decoder) => Load(Configuration.Default, stream, decoder); /// - /// Create a new instance of the class from the given stream. + /// Decode a new instance of the class from the given stream. + /// The pixel format is selected by the decoder. + /// + /// The config for the decoder. + /// The stream containing image information. + /// The decoder. + /// Thrown if the stream is not readable. + /// A new .> + public static Image Load(Configuration config, Stream stream, IImageDecoder decoder) => + WithSeekableStream(config, stream, s => decoder.Decode(config, s)); + + /// + /// Decode a new instance of the class from the given stream. /// /// The config for the decoder. /// The stream containing image information. /// Thrown if the stream is not readable. - /// A new .> - public static Image Load(Configuration config, Stream stream) => Load(config, stream); + /// A new .> + public static Image Load(Configuration config, Stream stream) => Load(config, stream, out _); /// /// Create a new instance of the class from the given stream. @@ -183,7 +198,16 @@ namespace SixLabors.ImageSharp throw new NotSupportedException(sb.ToString()); } - private static Image Load(Configuration config, Stream stream, out IImageFormat format) + /// + /// Decode a new instance of the class from the given stream. + /// The pixel format is selected by the decoder. + /// + /// The configuration options. + /// The stream containing image information. + /// the mime type of the decoded image. + /// Thrown if the stream is not readable. + /// A new . + public static Image Load(Configuration config, Stream stream, out IImageFormat format) { config = config ?? Configuration.Default; (Image img, IImageFormat format) data = WithSeekableStream(config, stream, s => Decode(s, config)); diff --git a/tests/ImageSharp.Tests/Drawing/RecolorImageTest.cs b/tests/ImageSharp.Tests/Drawing/RecolorImageTest.cs index 2dcd8b3d34..b064d9c964 100644 --- a/tests/ImageSharp.Tests/Drawing/RecolorImageTest.cs +++ b/tests/ImageSharp.Tests/Drawing/RecolorImageTest.cs @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Tests foreach (TestFile file in Files) { - using (Image image = file.CreateImage()) + using (Image image = file.CreateRgba32Image()) { image.Mutate(x => x.Fill(brush)); image.Save($"{path}/{file.FileName}"); @@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Tests foreach (TestFile file in Files) { - using (Image image = file.CreateImage()) + using (Image image = file.CreateRgba32Image()) { int imageHeight = image.Height; image.Mutate(x => x.Fill(brush, new Rectangle(0, imageHeight / 2 - imageHeight / 4, image.Width, imageHeight / 2))); diff --git a/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs index d7fb0a3d37..5660518eb4 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs @@ -96,7 +96,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing new Vector2(50, 300) }; - using (Image brushImage = TestFile.Create(TestImages.Bmp.Car).CreateImage()) + using (Image brushImage = TestFile.Create(TestImages.Bmp.Car).CreateRgba32Image()) using (var image = new Image(500, 500)) { var brush = new ImageBrush(brushImage); diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs index 7e054734e3..df3029a7fc 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs @@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp var options = new BmpEncoder(); var testFile = TestFile.Create(imagePath); - using (Image input = testFile.CreateImage()) + using (Image input = testFile.CreateRgba32Image()) { using (var memStream = new MemoryStream()) { @@ -74,7 +74,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp var options = new BmpEncoder(); var testFile = TestFile.Create(imagePath); - using (Image input = testFile.CreateImage()) + using (Image input = testFile.CreateRgba32Image()) { using (var memStream = new MemoryStream()) { diff --git a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs index e8fdbd8926..b2f9788ae4 100644 --- a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs +++ b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs @@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Tests foreach (TestFile file in Files) { - using (Image image = file.CreateImage()) + using (Image image = file.CreateRgba32Image()) { string filename = path + "/" + file.FileNameWithoutExtension + ".txt"; File.WriteAllText(filename, image.ToBase64String(PngFormat.Instance)); @@ -55,7 +55,7 @@ namespace SixLabors.ImageSharp.Tests foreach (TestFile file in Files) { - using (Image image = file.CreateImage()) + using (Image image = file.CreateRgba32Image()) { image.Save($"{path}/{file.FileName}"); } @@ -102,7 +102,7 @@ namespace SixLabors.ImageSharp.Tests foreach (TestFile file in Files) { - using (Image image = file.CreateImage()) + using (Image image = file.CreateRgba32Image()) { using (FileStream output = File.OpenWrite($"{path}/{file.FileNameWithoutExtension}.bmp")) { diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs index b13362aa3b..784f7ce703 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs @@ -165,7 +165,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif var testFile = TestFile.Create(TestImages.Gif.Rings); - using (Image image = testFile.CreateImage(options)) + using (Image image = testFile.CreateRgba32Image(options)) { Assert.Equal(1, image.Metadata.Properties.Count); Assert.Equal("Comments", image.Metadata.Properties[0].Name); @@ -183,7 +183,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif var testFile = TestFile.Create(TestImages.Gif.Rings); - using (Image image = testFile.CreateImage(options)) + using (Image image = testFile.CreateRgba32Image(options)) { Assert.Equal(0, image.Metadata.Properties.Count); } @@ -199,7 +199,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif var testFile = TestFile.Create(TestImages.Gif.Rings); - using (Image image = testFile.CreateImage(options)) + using (Image image = testFile.CreateRgba32Image(options)) { Assert.Equal(1, image.Metadata.Properties.Count); Assert.Equal("浉条卥慨灲", image.Metadata.Properties[0].Value); diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs index 5da1c3bb78..eab30944e9 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs @@ -58,7 +58,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif var options = new GifEncoder(); var testFile = TestFile.Create(imagePath); - using (Image input = testFile.CreateImage()) + using (Image input = testFile.CreateRgba32Image()) { using (var memStream = new MemoryStream()) { @@ -83,7 +83,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif var testFile = TestFile.Create(TestImages.Gif.Rings); - using (Image input = testFile.CreateImage()) + using (Image input = testFile.CreateRgba32Image()) { using (var memStream = new MemoryStream()) { @@ -107,7 +107,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif var testFile = TestFile.Create(TestImages.Gif.Rings); - using (Image input = testFile.CreateImage()) + using (Image input = testFile.CreateRgba32Image()) { input.Metadata.Properties.Clear(); using (var memStream = new MemoryStream()) diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.MetaData.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.MetaData.cs index 48acc9ea47..308cf28b35 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.MetaData.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.MetaData.cs @@ -216,7 +216,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg // Snake.jpg has both Exif and ICC profiles defined: var testFile = TestFile.Create(TestImages.Jpeg.Baseline.Snake); - using (Image image = testFile.CreateImage(decoder)) + using (Image image = testFile.CreateRgba32Image(decoder)) { if (ignoreMetaData) { diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs index 618947130a..d9013b507e 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs @@ -48,7 +48,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg var options = new JpegEncoder(); var testFile = TestFile.Create(imagePath); - using (Image input = testFile.CreateImage()) + using (Image input = testFile.CreateRgba32Image()) { using (var memStream = new MemoryStream()) { @@ -137,7 +137,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg var testFile = TestFile.Create(TestImages.Jpeg.Baseline.Calliphora); - using (Image input = testFile.CreateImage()) + using (Image input = testFile.CreateRgba32Image()) using (var memStream0 = new MemoryStream()) using (var memStream1 = new MemoryStream()) { @@ -160,7 +160,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg var testFile = TestFile.Create(TestImages.Jpeg.Baseline.Calliphora); - using (Image input = testFile.CreateImage()) + using (Image input = testFile.CreateRgba32Image()) using (var memStream0 = new MemoryStream()) using (var memStream1 = new MemoryStream()) { @@ -180,7 +180,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg var options = new JpegEncoder(); var testFile = TestFile.Create(imagePath); - using (Image input = testFile.CreateImage()) + using (Image input = testFile.CreateRgba32Image()) { using (var memStream = new MemoryStream()) { diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index 0dbccd2509..70d191d74e 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -202,7 +202,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png var testFile = TestFile.Create(TestImages.Png.Blur); - using (Image image = testFile.CreateImage(options)) + using (Image image = testFile.CreateRgba32Image(options)) { Assert.Equal(1, image.Metadata.Properties.Count); Assert.Equal("Software", image.Metadata.Properties[0].Name); @@ -220,7 +220,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png var testFile = TestFile.Create(TestImages.Png.Blur); - using (Image image = testFile.CreateImage(options)) + using (Image image = testFile.CreateRgba32Image(options)) { Assert.Equal(0, image.Metadata.Properties.Count); } @@ -236,7 +236,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png var testFile = TestFile.Create(TestImages.Png.Blur); - using (Image image = testFile.CreateImage(options)) + using (Image image = testFile.CreateRgba32Image(options)) { Assert.Equal(1, image.Metadata.Properties.Count); Assert.Equal("潓瑦慷敲", image.Metadata.Properties[0].Name); diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index 5aa69dd6af..b8178fd4f3 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -219,7 +219,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png var options = new PngEncoder(); var testFile = TestFile.Create(imagePath); - using (Image input = testFile.CreateImage()) + using (Image input = testFile.CreateRgba32Image()) { using (var memStream = new MemoryStream()) { @@ -244,7 +244,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png var options = new PngEncoder(); var testFile = TestFile.Create(imagePath); - using (Image input = testFile.CreateImage()) + using (Image input = testFile.CreateRgba32Image()) { using (var memStream = new MemoryStream()) { @@ -268,7 +268,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png var options = new PngEncoder(); var testFile = TestFile.Create(imagePath); - using (Image input = testFile.CreateImage()) + using (Image input = testFile.CreateRgba32Image()) { PngMetadata inMeta = input.Metadata.GetFormatMetadata(PngFormat.Instance); Assert.True(inMeta.HasTrans); diff --git a/tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.cs b/tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.cs index aabc3f50e5..ec6705d0ec 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.cs @@ -16,7 +16,9 @@ namespace SixLabors.ImageSharp.Tests { public abstract class ImageLoadTestBase : IDisposable { - protected Image returnImage; + protected Image localStreamReturnImageRgba32; + + protected Image localStreamReturnImageAgnostic; protected Mock localDecoder; @@ -48,12 +50,14 @@ namespace SixLabors.ImageSharp.Tests protected ImageLoadTestBase() { - this.returnImage = new Image(1, 1); + this.localStreamReturnImageRgba32 = new Image(1, 1); + this.localStreamReturnImageAgnostic = new Image(1, 1); this.localImageFormatMock = new Mock(); this.localDecoder = new Mock(); this.localMimeTypeDetector = new MockImageFormatDetector(this.localImageFormatMock.Object); + this.localDecoder.Setup(x => x.Decode(It.IsAny(), It.IsAny())) .Callback((c, s) => { @@ -63,7 +67,19 @@ namespace SixLabors.ImageSharp.Tests this.DecodedData = ms.ToArray(); } }) - .Returns(this.returnImage); + .Returns(this.localStreamReturnImageRgba32); + + this.localDecoder.Setup(x => x.Decode(It.IsAny(), It.IsAny())) + .Callback((c, s) => + { + using (var ms = new MemoryStream()) + { + s.CopyTo(ms); + this.DecodedData = ms.ToArray(); + } + }) + .Returns(this.localStreamReturnImageAgnostic); + this.LocalConfiguration = new Configuration { @@ -85,7 +101,8 @@ namespace SixLabors.ImageSharp.Tests public void Dispose() { // clean up the global object; - this.returnImage?.Dispose(); + this.localStreamReturnImageRgba32?.Dispose(); + this.localStreamReturnImageAgnostic?.Dispose(); } } } diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath.cs b/tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath.cs index 1a21d3d105..d6fe43897c 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath.cs @@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Tests Assert.NotNull(img); - this.TestFormat.VerifyDecodeCall(this.Marker, this.TopLevelConfiguration); + this.TestFormat.VerifySpecificDecodeCall(this.Marker, this.TopLevelConfiguration); } [Fact] diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes.cs b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes.cs deleted file mode 100644 index eed1a28252..0000000000 --- a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes.cs +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.IO; - -using Moq; - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; - -using Xunit; - -// ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Tests -{ - public partial class ImageTests - { - public class Load_FromBytes : ImageLoadTestBase - { - private byte[] ByteArray => this.DataStream.ToArray(); - - private ReadOnlySpan ByteSpan => this.ByteArray.AsSpan(); - - private byte[] ActualImageBytes => TestFile.Create(TestImages.Bmp.F).Bytes; - - private ReadOnlySpan ActualImageSpan => this.ActualImageBytes.AsSpan(); - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void BasicCase(bool useSpan) - { - Image img = useSpan - ? Image.Load(this.TopLevelConfiguration, this.ByteSpan) - : Image.Load(this.TopLevelConfiguration, this.ByteArray); - - Assert.NotNull(img); - Assert.Equal(this.TestFormat.Sample(), img); - - this.TestFormat.VerifyDecodeCall(this.Marker, this.TopLevelConfiguration); - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void NonDefaultPixelType(bool useSpan) - { - Image img = useSpan - ? Image.Load(this.TopLevelConfiguration, this.ByteSpan) - : Image.Load(this.TopLevelConfiguration, this.ByteArray); - - Assert.NotNull(img); - Assert.Equal(this.TestFormat.Sample(), img); - - this.TestFormat.VerifyDecodeCall(this.Marker, this.TopLevelConfiguration); - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void UseLocalConfiguration(bool useSpan) - { - Image img = useSpan - ? Image.Load(this.LocalConfiguration, this.ByteSpan) - : Image.Load(this.LocalConfiguration, this.ByteArray); - - Assert.NotNull(img); - - this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, It.IsAny())); - - Assert.Equal(this.DataStream.ToArray(), this.DecodedData); - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void UseCustomDecoder(bool useSpan) - { - Image img = useSpan - ? Image.Load( - this.TopLevelConfiguration, - this.ByteSpan, - this.localDecoder.Object) - : Image.Load( - this.TopLevelConfiguration, - this.ByteArray, - this.localDecoder.Object); - Assert.NotNull(img); - this.localDecoder.Verify(x => x.Decode(this.TopLevelConfiguration, It.IsAny())); - Assert.Equal(this.DataStream.ToArray(), this.DecodedData); - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void UseGlobalConfiguration(bool useSpan) - { - using (Image img = - useSpan ? Image.Load(this.ActualImageSpan) : Image.Load(this.ActualImageBytes)) - { - Assert.Equal(new Size(108, 202), img.Size()); - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void UseGlobalConfiguration_NonDefaultPixelType(bool useSpan) - { - using (Image img = useSpan - ? Image.Load(this.ActualImageSpan) - : Image.Load(this.ActualImageBytes)) - { - Assert.Equal(new Size(108, 202), img.Size()); - } - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_PassLocalConfiguration.cs b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_PassLocalConfiguration.cs new file mode 100644 index 0000000000..5fe87fedca --- /dev/null +++ b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_PassLocalConfiguration.cs @@ -0,0 +1,119 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.PixelFormats; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests +{ + public partial class ImageTests + { + public class Load_FromBytes_PassLocalConfiguration : ImageLoadTestBase + { + private byte[] ByteArray => this.DataStream.ToArray(); + + private ReadOnlySpan ByteSpan => this.ByteArray.AsSpan(); + + private byte[] ActualImageBytes => TestFile.Create(TestImages.Bmp.F).Bytes; + + private ReadOnlySpan ActualImageSpan => this.ActualImageBytes.AsSpan(); + + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void Configuration_Bytes_Specific(bool useSpan) + { + var img = useSpan + ? Image.Load(this.TopLevelConfiguration, this.ByteSpan) + : Image.Load(this.TopLevelConfiguration, this.ByteArray); + + Assert.NotNull(img); + Assert.Equal(this.TestFormat.Sample(), img); + + this.TestFormat.VerifySpecificDecodeCall(this.Marker, this.TopLevelConfiguration); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void Configuration_Bytes_Agnostic(bool useSpan) + { + var img = useSpan + ? Image.Load(this.TopLevelConfiguration, this.ByteSpan) + : Image.Load(this.TopLevelConfiguration, this.ByteArray); + + Assert.NotNull(img); + Assert.Equal(this.TestFormat.SampleAgnostic(), img); + + this.TestFormat.VerifyAgnosticDecodeCall(this.Marker, this.TopLevelConfiguration); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void Configuration_Bytes_Decoder_Specific(bool useSpan) + { + TestFormat localFormat = new TestFormat(); + + var img = useSpan ? + Image.Load(this.TopLevelConfiguration, this.ByteSpan, localFormat.Decoder) : + Image.Load(this.TopLevelConfiguration, this.ByteArray, localFormat.Decoder); + + Assert.NotNull(img); + localFormat.VerifySpecificDecodeCall(this.Marker, this.TopLevelConfiguration); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void Configuration_Bytes_Decoder_Agnostic(bool useSpan) + { + TestFormat localFormat = new TestFormat(); + + var img = useSpan ? + Image.Load(this.TopLevelConfiguration, this.ByteSpan, localFormat.Decoder) : + Image.Load(this.TopLevelConfiguration, this.ByteArray, localFormat.Decoder); + + Assert.NotNull(img); + localFormat.VerifyAgnosticDecodeCall(this.Marker, this.TopLevelConfiguration); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void Configuration_Bytes_OutFormat_Specific(bool useSpan) + { + IImageFormat format; + var img = useSpan ? + Image.Load(this.TopLevelConfiguration, this.ByteSpan, out format) : + Image.Load(this.TopLevelConfiguration, this.ByteArray, out format); + + Assert.NotNull(img); + Assert.Equal(this.TestFormat, format); + + this.TestFormat.VerifySpecificDecodeCall(this.Marker, this.TopLevelConfiguration); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void Configuration_Bytes_OutFormat_Agnostic(bool useSpan) + { + IImageFormat format; + var img = useSpan ? + Image.Load(this.TopLevelConfiguration, this.ByteSpan, out format) : + Image.Load(this.TopLevelConfiguration, this.ByteArray, out format); + + Assert.NotNull(img); + Assert.Equal(this.TestFormat, format); + + this.TestFormat.VerifyAgnosticDecodeCall(this.Marker, this.TopLevelConfiguration); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_UseGlobalConfiguration.cs b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_UseGlobalConfiguration.cs new file mode 100644 index 0000000000..23c25d9dbb --- /dev/null +++ b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_UseGlobalConfiguration.cs @@ -0,0 +1,101 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; + +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests +{ + public partial class ImageTests + { + public class Load_FromBytes_UseGlobalConfiguration + { + private static byte[] ByteArray { get; } = TestFile.Create(TestImages.Bmp.F).Bytes; + + private static Span ByteSpan => new Span(ByteArray); + + private static void VerifyDecodedImage(Image img) + { + Assert.Equal(new Size(108, 202), img.Size()); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void Bytes_Specific(bool useSpan) + { + using (var img = useSpan ? Image.Load(ByteSpan) : Image.Load(ByteArray)) + { + VerifyDecodedImage(img); + } + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void Bytes_Agnostic(bool useSpan) + { + using (var img = useSpan ? Image.Load(ByteSpan) : Image.Load(ByteArray)) + { + VerifyDecodedImage(img); + } + } + + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void Bytes_Decoder_Specific(bool useSpan) + { + using (var img = useSpan ? Image.Load(ByteSpan, new BmpDecoder()) : Image.Load(ByteArray, new BmpDecoder())) + { + VerifyDecodedImage(img); + } + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void Bytes_Decoder_Agnostic(bool useSpan) + { + using (var img = useSpan ? Image.Load(ByteSpan, new BmpDecoder()) : Image.Load(ByteArray, new BmpDecoder())) + { + VerifyDecodedImage(img); + } + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void Bytes_OutFormat_Specific(bool useSpan) + { + IImageFormat format; + using (var img = useSpan ? Image.Load(ByteSpan, out format) : Image.Load(ByteArray, out format)) + { + VerifyDecodedImage(img); + Assert.IsType(format); + } + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void Bytes_OutFormat_Agnostic(bool useSpan) + { + IImageFormat format; + using (var img = useSpan ? Image.Load(ByteSpan, out format) : Image.Load(ByteArray, out format)) + { + VerifyDecodedImage(img); + Assert.IsType(format); + } + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream.cs b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream.cs deleted file mode 100644 index 6b6acb1b80..0000000000 --- a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream.cs +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.IO; - -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.PixelFormats; - -using Xunit; -// ReSharper disable InconsistentNaming - -namespace SixLabors.ImageSharp.Tests -{ - using SixLabors.Primitives; - - public partial class ImageTests - { - /// - /// Tests the class. - /// - public class Load_FromStream : ImageLoadTestBase - { - [Fact] - public void BasicCase() - { - var img = Image.Load(this.TopLevelConfiguration, this.DataStream); - - Assert.NotNull(img); - Assert.Equal(this.TestFormat.Sample(), img); - - this.TestFormat.VerifyDecodeCall(this.Marker, this.TopLevelConfiguration); - } - - [Fact] - public void UseGlobalConfiguration() - { - byte[] data = TestFile.Create(TestImages.Bmp.F).Bytes; - - using (var stream = new MemoryStream(data)) - using (var img = Image.Load(stream)) - { - Assert.Equal(new Size(108, 202), img.Size()); - } - } - - [Fact] - public void NonDefaultPixelTypeImage() - { - var img = Image.Load(this.TopLevelConfiguration, this.DataStream); - - Assert.NotNull(img); - Assert.Equal(this.TestFormat.Sample(), img); - - this.TestFormat.VerifyDecodeCall(this.Marker, this.TopLevelConfiguration); - } - - [Fact] - public void NonSeekableStream() - { - var stream = new NoneSeekableStream(this.DataStream); - var img = Image.Load(this.TopLevelConfiguration, stream); - - Assert.NotNull(img); - - this.TestFormat.VerifyDecodeCall(this.Marker, this.TopLevelConfiguration); - } - - [Fact] - public void UseLocalConfiguration() - { - Stream stream = new MemoryStream(); - var img = Image.Load(this.LocalConfiguration, stream); - - Assert.NotNull(img); - - this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, stream)); - } - - [Fact] - public void UseCustomDecoder() - { - Stream stream = new MemoryStream(); - var img = Image.Load(this.TopLevelConfiguration, stream, this.localDecoder.Object); - - Assert.NotNull(img); - this.localDecoder.Verify(x => x.Decode(this.TopLevelConfiguration, stream)); - } - - // TODO: This should be a png decoder test! - [Fact] - public void LoadsImageWithoutThrowingCrcException() - { - var image1Provider = TestImageProvider.File(TestImages.Png.VersioningImage1); - - using (Image img = image1Provider.GetImage()) - { - Assert.Equal(166036, img.Frames.RootFrame.GetPixelSpan().Length); - } - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_PassLocalConfiguration.cs b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_PassLocalConfiguration.cs new file mode 100644 index 0000000000..5ca86c3cee --- /dev/null +++ b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_PassLocalConfiguration.cs @@ -0,0 +1,93 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.IO; + +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.PixelFormats; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests +{ + public partial class ImageTests + { + public class Load_FromStream_PassLocalConfiguration : ImageLoadTestBase + { + [Fact] + public void Configuration_Stream_Specific() + { + var img = Image.Load(this.TopLevelConfiguration, this.DataStream); + + Assert.NotNull(img); + Assert.Equal(this.TestFormat.Sample(), img); + + this.TestFormat.VerifySpecificDecodeCall(this.Marker, this.TopLevelConfiguration); + } + + [Fact] + public void Configuration_Stream_Agnostic() + { + var img = Image.Load(this.TopLevelConfiguration, this.DataStream); + + Assert.NotNull(img); + Assert.Equal(this.TestFormat.SampleAgnostic(), img); + + this.TestFormat.VerifyAgnosticDecodeCall(this.Marker, this.TopLevelConfiguration); + } + + [Fact] + public void NonSeekableStream() + { + var stream = new NoneSeekableStream(this.DataStream); + var img = Image.Load(this.TopLevelConfiguration, stream); + + Assert.NotNull(img); + + this.TestFormat.VerifySpecificDecodeCall(this.Marker, this.TopLevelConfiguration); + } + + [Fact] + public void Configuration_Stream_Decoder_Specific() + { + Stream stream = new MemoryStream(); + var img = Image.Load(this.TopLevelConfiguration, stream, this.localDecoder.Object); + + Assert.NotNull(img); + this.localDecoder.Verify(x => x.Decode(this.TopLevelConfiguration, stream)); + } + + [Fact] + public void Configuration_Stream_Decoder_Agnostic() + { + Stream stream = new MemoryStream(); + var img = Image.Load(this.TopLevelConfiguration, stream, this.localDecoder.Object); + + Assert.NotNull(img); + this.localDecoder.Verify(x => x.Decode(this.TopLevelConfiguration, stream)); + } + + [Fact] + public void Configuration_Stream_OutFormat_Specific() + { + var img = Image.Load(this.TopLevelConfiguration, this.DataStream, out IImageFormat format); + + Assert.NotNull(img); + Assert.Equal(this.TestFormat, format); + + this.TestFormat.VerifySpecificDecodeCall(this.Marker, this.TopLevelConfiguration); + } + + [Fact] + public void Configuration_Stream_OutFormat_Agnostic() + { + var img = Image.Load(this.TopLevelConfiguration, this.DataStream, out IImageFormat format); + + Assert.NotNull(img); + Assert.Equal(this.TestFormat, format); + + this.TestFormat.VerifyAgnosticDecodeCall(this.Marker, this.TopLevelConfiguration); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_UseDefaultConfiguration.cs b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_UseDefaultConfiguration.cs new file mode 100644 index 0000000000..27aba42ead --- /dev/null +++ b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_UseDefaultConfiguration.cs @@ -0,0 +1,95 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.IO; + +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests +{ + public partial class ImageTests + { + public class Load_FromStream_UseDefaultConfiguration + { + [Fact] + public void Stream_Specific() + { + byte[] data = TestFile.Create(TestImages.Bmp.F).Bytes; + + using (var stream = new MemoryStream(data)) + using (var img = Image.Load(stream)) + { + Assert.Equal(new Size(108, 202), img.Size()); + } + } + + [Fact] + public void Stream_Agnostic() + { + byte[] data = TestFile.Create(TestImages.Bmp.F).Bytes; + + using (var stream = new MemoryStream(data)) + using (var img = Image.Load(stream)) + { + Assert.Equal(new Size(108, 202), img.Size()); + } + } + + [Fact] + public void Stream_OutFormat_Specific() + { + byte[] data = TestFile.Create(TestImages.Bmp.F).Bytes; + + + using (var stream = new MemoryStream(data)) + using (var img = Image.Load(stream, out IImageFormat format)) + { + Assert.Equal(new Size(108, 202), img.Size()); + Assert.IsType(format); + } + } + + [Fact] + public void Stream_Decoder_Specific() + { + byte[] data = TestFile.Create(TestImages.Bmp.F).Bytes; + + using (var stream = new MemoryStream(data)) + using (var img = Image.Load(stream, new BmpDecoder())) + { + Assert.Equal(new Size(108, 202), img.Size()); + } + } + + [Fact] + public void Stream_Decoder_Agnostic() + { + byte[] data = TestFile.Create(TestImages.Bmp.F).Bytes; + + using (var stream = new MemoryStream(data)) + using (var img = Image.Load(stream, new BmpDecoder())) + { + Assert.Equal(new Size(108, 202), img.Size()); + } + } + + [Fact] + public void Stream_OutFormat_Agnostic() + { + byte[] data = TestFile.Create(TestImages.Bmp.F).Bytes; + + using (var stream = new MemoryStream(data)) + using (var img = Image.Load(stream, out IImageFormat format)) + { + Assert.Equal(new Size(108, 202), img.Size()); + Assert.IsType(format); + } + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs index 9d145f3805..668e699c5f 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs @@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Tests [InlineData(TestImageWriteFormat.Png)] public void Constructor(TestImageWriteFormat imageFormat) { - Image image = TestFile.Create(TestImages.Jpeg.Baseline.Calliphora).CreateImage(); + Image image = TestFile.Create(TestImages.Jpeg.Baseline.Calliphora).CreateRgba32Image(); Assert.Null(image.Metadata.ExifProfile); @@ -126,7 +126,7 @@ namespace SixLabors.ImageSharp.Tests [InlineData(TestImageWriteFormat.Png)] public void ReadWriteInfinity(TestImageWriteFormat imageFormat) { - Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateImage(); + Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateRgba32Image(); image.Metadata.ExifProfile.SetValue(ExifTag.ExposureBiasValue, new SignedRational(double.PositiveInfinity)); image = WriteAndReadJpeg(image); @@ -156,7 +156,7 @@ namespace SixLabors.ImageSharp.Tests { var latitude = new Rational[] { new Rational(12.3), new Rational(4.56), new Rational(789.0) }; - Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateImage(); + Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateRgba32Image(); image.Metadata.ExifProfile.SetValue(ExifTag.Software, "ImageSharp"); ExifValue value = image.Metadata.ExifProfile.GetValue(ExifTag.Software); @@ -314,7 +314,7 @@ namespace SixLabors.ImageSharp.Tests // This image contains an 802 byte EXIF profile // It has a tag with an index offset of 18,481,152 bytes (overrunning the data) - Image image = TestFile.Create(TestImages.Jpeg.Progressive.Bad.ExifUndefType).CreateImage(); + Image image = TestFile.Create(TestImages.Jpeg.Progressive.Bad.ExifUndefType).CreateRgba32Image(); Assert.NotNull(image); ExifProfile profile = image.Metadata.ExifProfile; @@ -333,7 +333,7 @@ namespace SixLabors.ImageSharp.Tests public void TestArrayValueWithUnspecifiedSize() { // This images contains array in the exif profile that has zero components. - Image image = TestFile.Create(TestImages.Jpeg.Issues.InvalidCast520).CreateImage(); + Image image = TestFile.Create(TestImages.Jpeg.Issues.InvalidCast520).CreateRgba32Image(); ExifProfile profile = image.Metadata.ExifProfile; Assert.NotNull(profile); @@ -408,7 +408,7 @@ namespace SixLabors.ImageSharp.Tests internal static ExifProfile GetExifProfile() { - Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateImage(); + Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateRgba32Image(); ExifProfile profile = image.Metadata.ExifProfile; Assert.NotNull(profile); diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifValueTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifValueTests.cs index 4327cae335..8d786811cf 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifValueTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifValueTests.cs @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Tests private static ExifValue GetExifValue() { ExifProfile profile; - using (Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateImage()) + using (Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateRgba32Image()) { profile = image.Metadata.ExifProfile; } diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs index 9c3ea90d53..0c7a760818 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs @@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders public void PorterDuffOutputIsCorrect(TestImageProvider provider, PixelAlphaCompositionMode mode) { var srcFile = TestFile.Create(TestImages.Png.PDSrc); - using (Image src = srcFile.CreateImage()) + using (Image src = srcFile.CreateRgba32Image()) using (Image dest = provider.GetImage()) { GraphicsOptions options = new GraphicsOptions diff --git a/tests/ImageSharp.Tests/TestFile.cs b/tests/ImageSharp.Tests/TestFile.cs index 089249e217..6ca86ced6e 100644 --- a/tests/ImageSharp.Tests/TestFile.cs +++ b/tests/ImageSharp.Tests/TestFile.cs @@ -134,7 +134,7 @@ namespace SixLabors.ImageSharp.Tests /// /// The . /// - public Image CreateImage() + public Image CreateRgba32Image() { return this.Image.Clone(); } @@ -145,9 +145,9 @@ namespace SixLabors.ImageSharp.Tests /// /// The . /// - public Image CreateImage(IImageDecoder decoder) + public Image CreateRgba32Image(IImageDecoder decoder) { - return ImageSharp.Image.Load(this.Image.GetConfiguration(), this.Bytes, decoder); + return ImageSharp.Image.Load(this.Image.GetConfiguration(), this.Bytes, decoder); } } } diff --git a/tests/ImageSharp.Tests/TestFileSystem.cs b/tests/ImageSharp.Tests/TestFileSystem.cs index 21ad4d2c1a..9211e70f79 100644 --- a/tests/ImageSharp.Tests/TestFileSystem.cs +++ b/tests/ImageSharp.Tests/TestFileSystem.cs @@ -12,29 +12,24 @@ namespace SixLabors.ImageSharp.Tests /// public class TestFileSystem : ImageSharp.IO.IFileSystem { - - public static TestFileSystem Global { get; } = new TestFileSystem(); - - public static void RegisterGlobalTestFormat() - { - Configuration.Default.FileSystem = Global; - } - - Dictionary fileSystem = new Dictionary(StringComparer.OrdinalIgnoreCase); + private readonly Dictionary fileSystem = new Dictionary(StringComparer.OrdinalIgnoreCase); public void AddFile(string path, Stream data) { - fileSystem.Add(path, data); + lock (this.fileSystem) + { + this.fileSystem.Add(path, data); + } } public Stream Create(string path) { // if we have injected a fake file use it instead - lock (fileSystem) + lock (this.fileSystem) { - if (fileSystem.ContainsKey(path)) + if (this.fileSystem.ContainsKey(path)) { - Stream stream = fileSystem[path]; + Stream stream = this.fileSystem[path]; stream.Position = 0; return stream; } @@ -43,15 +38,14 @@ namespace SixLabors.ImageSharp.Tests return File.Create(path); } - public Stream OpenRead(string path) { // if we have injected a fake file use it instead - lock (fileSystem) + lock (this.fileSystem) { - if (fileSystem.ContainsKey(path)) + if (this.fileSystem.ContainsKey(path)) { - Stream stream = fileSystem[path]; + Stream stream = this.fileSystem[path]; stream.Position = 0; return stream; } @@ -60,5 +54,4 @@ namespace SixLabors.ImageSharp.Tests return File.OpenRead(path); } } -} - +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestFormat.cs b/tests/ImageSharp.Tests/TestFormat.cs index 91e31b356e..23bd0a54c3 100644 --- a/tests/ImageSharp.Tests/TestFormat.cs +++ b/tests/ImageSharp.Tests/TestFormat.cs @@ -6,6 +6,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Numerics; using System.Reflection; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; @@ -18,6 +19,8 @@ namespace SixLabors.ImageSharp.Tests /// public class TestFormat : IConfigurationModule, IImageFormat { + private readonly Dictionary sampleImages = new Dictionary(); + // We should not change Configuration.Default in individual tests! // Create new configuration instances with new Configuration(TestFormat.GlobalTestFormat) instead! public static TestFormat GlobalTestFormat { get; } = new TestFormat(); @@ -49,12 +52,23 @@ namespace SixLabors.ImageSharp.Tests return ms; } - Dictionary _sampleImages = new Dictionary(); + public void VerifySpecificDecodeCall(byte[] marker, Configuration config) + where TPixel : struct, IPixel + { + DecodeOperation[] discovered = this.DecodeCalls.Where(x => x.IsMatch(marker, config, typeof(TPixel))).ToArray(); - public void VerifyDecodeCall(byte[] marker, Configuration config) + Assert.True(discovered.Any(), "No calls to decode on this formate with the proveded options happend"); + + foreach (DecodeOperation d in discovered) + { + this.DecodeCalls.Remove(d); + } + } + + public void VerifyAgnosticDecodeCall(byte[] marker, Configuration config) { - DecodeOperation[] discovered = this.DecodeCalls.Where(x => x.IsMatch(marker, config)).ToArray(); + DecodeOperation[] discovered = this.DecodeCalls.Where(x => x.IsMatch(marker, config, typeof(TestPixelForAgnosticDecode))).ToArray(); Assert.True(discovered.Any(), "No calls to decode on this formate with the proveded options happend"); @@ -68,17 +82,19 @@ namespace SixLabors.ImageSharp.Tests public Image Sample() where TPixel : struct, IPixel { - lock (this._sampleImages) + lock (this.sampleImages) { - if (!this._sampleImages.ContainsKey(typeof(TPixel))) + if (!this.sampleImages.ContainsKey(typeof(TPixel))) { - this._sampleImages.Add(typeof(TPixel), new Image(1, 1)); + this.sampleImages.Add(typeof(TPixel), new Image(1, 1)); } - return (Image)this._sampleImages[typeof(TPixel)]; + return (Image)this.sampleImages[typeof(TPixel)]; } } + public Image SampleAgnostic() => this.Sample(); + public string MimeType => "img/test"; public string Extension => "test_ext"; @@ -123,10 +139,12 @@ namespace SixLabors.ImageSharp.Tests public byte[] marker; internal Configuration config; - public bool IsMatch(byte[] testMarker, Configuration config) + public Type pixelType; + + public bool IsMatch(byte[] testMarker, Configuration config, Type pixelType) { - if (this.config != config) + if (this.config != config || this.pixelType != pixelType) { return false; } @@ -191,7 +209,8 @@ namespace SixLabors.ImageSharp.Tests this.testFormat.DecodeCalls.Add(new DecodeOperation { marker = marker, - config = config + config = config, + pixelType = typeof(TPixel) }); // TODO record this happend so we can verify it. @@ -200,7 +219,7 @@ namespace SixLabors.ImageSharp.Tests public bool IsSupportedFileFormat(Span header) => testFormat.IsSupportedFileFormat(header); - public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); + public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); } public class TestEncoder : ImageSharp.Formats.IImageEncoder @@ -221,5 +240,27 @@ namespace SixLabors.ImageSharp.Tests // TODO record this happend so we can verify it. } } + + + struct TestPixelForAgnosticDecode : IPixel + { + public PixelOperations CreatePixelOperations() => new PixelOperations(); + public void FromScaledVector4(Vector4 vector) { } + public Vector4 ToScaledVector4() => default; + public void FromVector4(Vector4 vector) { } + public Vector4 ToVector4() => default; + public void FromArgb32(Argb32 source) { } + public void FromBgra5551(Bgra5551 source) { } + public void FromBgr24(Bgr24 source) { } + public void FromBgra32(Bgra32 source) { } + public void FromGray8(Gray8 source) { } + public void FromGray16(Gray16 source) { } + public void FromRgb24(Rgb24 source) { } + public void FromRgba32(Rgba32 source) { } + public void ToRgba32(ref Rgba32 dest) { } + public void FromRgb48(Rgb48 source) { } + public void FromRgba64(Rgba64 source) { } + public bool Equals(TestPixelForAgnosticDecode other) => false; + } } } diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceDecoderBenchmarks.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceDecoderBenchmarks.cs index 033f0866a3..15213d5d32 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceDecoderBenchmarks.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceDecoderBenchmarks.cs @@ -86,7 +86,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests { foreach (string testFile in testFiles) { - Image image = TestFile.Create(testFile).CreateImage(decoder); + Image image = TestFile.Create(testFile).CreateRgba32Image(decoder); image.Dispose(); } }, From 35640879f22c362f32d6a8e24a26cb09f5b6709b Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 5 May 2019 21:19:35 +0200 Subject: [PATCH 135/223] full coverage for Image.Load (I hope) --- .../Image/ImageTests.Load_FileSystemPath.cs | 83 -------------- ...d_FileSystemPath_PassLocalConfiguration.cs | 100 +++++++++++++++++ ..._FileSystemPath_UseDefaultConfiguration.cs | 103 ++++++++++++++++++ ...s.Load_FromBytes_UseGlobalConfiguration.cs | 4 +- ...Load_FromStream_UseDefaultConfiguration.cs | 60 +++++----- .../ImageSharp.Tests/ImageSharp.Tests.csproj | 3 +- 6 files changed, 234 insertions(+), 119 deletions(-) delete mode 100644 tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath.cs create mode 100644 tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath_PassLocalConfiguration.cs create mode 100644 tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath_UseDefaultConfiguration.cs diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath.cs b/tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath.cs deleted file mode 100644 index d6fe43897c..0000000000 --- a/tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath.cs +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using SixLabors.ImageSharp.PixelFormats; -using Xunit; -// ReSharper disable InconsistentNaming - -namespace SixLabors.ImageSharp.Tests -{ - using Moq; - - using SixLabors.ImageSharp.IO; - - public partial class ImageTests - { - public class Load_FileSystemPath : ImageLoadTestBase - { - [Fact] - public void BasicCase() - { - var img = Image.Load(this.TopLevelConfiguration, this.MockFilePath); - - Assert.NotNull(img); - - this.TestFormat.VerifySpecificDecodeCall(this.Marker, this.TopLevelConfiguration); - } - - [Fact] - public void UseLocalConfiguration() - { - var img = Image.Load(this.LocalConfiguration, this.MockFilePath); - - Assert.NotNull(img); - - this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, this.DataStream)); - } - - [Fact] - public void UseCustomDecoder() - { - var img = Image.Load(this.TopLevelConfiguration, this.MockFilePath, this.localDecoder.Object); - - Assert.NotNull(img); - this.localDecoder.Verify(x => x.Decode(this.TopLevelConfiguration, this.DataStream)); - } - - - [Fact] - public void UseGlobalConfigration() - { - var file = TestFile.Create(TestImages.Bmp.Car); - using (var image = Image.Load(file.FullPath)) - { - Assert.Equal(600, image.Width); - Assert.Equal(450, image.Height); - } - } - - [Fact] - public void WhenFileNotFound_Throws() - { - System.IO.FileNotFoundException ex = Assert.Throws( - () => - { - Image.Load(Guid.NewGuid().ToString()); - }); - } - - [Fact] - public void WhenPathIsNull_Throws() - { - ArgumentNullException ex = Assert.Throws( - () => - { - Image.Load((string)null); - }); - } - } - - - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath_PassLocalConfiguration.cs b/tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath_PassLocalConfiguration.cs new file mode 100644 index 0000000000..92159f0c9a --- /dev/null +++ b/tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath_PassLocalConfiguration.cs @@ -0,0 +1,100 @@ +// // Copyright (c) Six Labors and contributors. +// // Licensed under the Apache License, Version 2.0. + +using System; + +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.PixelFormats; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests +{ + public partial class ImageTests + { + public class Load_FileSystemPath_PassLocalConfiguration : ImageLoadTestBase + { + [Fact] + public void Configuration_Path_Specific() + { + var img = Image.Load(this.TopLevelConfiguration, this.MockFilePath); + + Assert.NotNull(img); + Assert.Equal(this.TestFormat.Sample(), img); + + this.TestFormat.VerifySpecificDecodeCall(this.Marker, this.TopLevelConfiguration); + } + + [Fact] + public void Configuration_Path_Agnostic() + { + var img = Image.Load(this.TopLevelConfiguration, this.MockFilePath); + + Assert.NotNull(img); + Assert.Equal(this.TestFormat.SampleAgnostic(), img); + + this.TestFormat.VerifyAgnosticDecodeCall(this.Marker, this.TopLevelConfiguration); + } + + [Fact] + public void Configuration_Path_Decoder_Specific() + { + var img = Image.Load(this.TopLevelConfiguration, this.MockFilePath, this.localDecoder.Object); + + Assert.NotNull(img); + this.localDecoder.Verify(x => x.Decode(this.TopLevelConfiguration, this.DataStream)); + } + + [Fact] + public void Configuration_Path_Decoder_Agnostic() + { + var img = Image.Load(this.TopLevelConfiguration, this.MockFilePath, this.localDecoder.Object); + + Assert.NotNull(img); + this.localDecoder.Verify(x => x.Decode(this.TopLevelConfiguration, this.DataStream)); + } + + [Fact] + public void Configuration_Path_OutFormat_Specific() + { + var img = Image.Load(this.TopLevelConfiguration, this.MockFilePath, out IImageFormat format); + + Assert.NotNull(img); + Assert.Equal(this.TestFormat, format); + + this.TestFormat.VerifySpecificDecodeCall(this.Marker, this.TopLevelConfiguration); + } + + [Fact] + public void Configuration_Path_OutFormat_Agnostic() + { + var img = Image.Load(this.TopLevelConfiguration, this.MockFilePath, out IImageFormat format); + + Assert.NotNull(img); + Assert.Equal(this.TestFormat, format); + + this.TestFormat.VerifyAgnosticDecodeCall(this.Marker, this.TopLevelConfiguration); + } + + [Fact] + public void WhenFileNotFound_Throws() + { + System.IO.FileNotFoundException ex = Assert.Throws( + () => + { + Image.Load(this.TopLevelConfiguration, Guid.NewGuid().ToString()); + }); + } + + [Fact] + public void WhenPathIsNull_Throws() + { + ArgumentNullException ex = Assert.Throws( + () => + { + Image.Load(this.TopLevelConfiguration,(string)null); + }); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath_UseDefaultConfiguration.cs b/tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath_UseDefaultConfiguration.cs new file mode 100644 index 0000000000..19cf7ee647 --- /dev/null +++ b/tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath_UseDefaultConfiguration.cs @@ -0,0 +1,103 @@ +// // Copyright (c) Six Labors and contributors. +// // Licensed under the Apache License, Version 2.0. + +using System; + +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests +{ + public partial class ImageTests + { + public class Load_FileSystemPath_UseDefaultConfiguration + { + private string Path { get; } = TestFile.GetInputFileFullPath(TestImages.Bmp.Bit8); + + private static void VerifyDecodedImage(Image img) + { + Assert.Equal(new Size(127, 64), img.Size()); + } + + [Fact] + public void Path_Specific() + { + using (var img = Image.Load(this.Path)) + { + VerifyDecodedImage(img); + } + } + + [Fact] + public void Path_Agnostic() + { + using (var img = Image.Load(this.Path)) + { + VerifyDecodedImage(img); + } + } + + + [Fact] + public void Path_Decoder_Specific() + { + using (var img = Image.Load(this.Path, new BmpDecoder())) + { + VerifyDecodedImage(img); + } + } + + [Fact] + public void Path_Decoder_Agnostic() + { + using (var img = Image.Load(this.Path, new BmpDecoder())) + { + VerifyDecodedImage(img); + } + } + + [Fact] + public void Path_OutFormat_Specific() + { + using (var img = Image.Load(this.Path, out IImageFormat format)) + { + VerifyDecodedImage(img); + Assert.IsType(format); + } + } + + [Fact] + public void Path_OutFormat_Agnostic() + { + using (var img = Image.Load(this.Path, out IImageFormat format)) + { + VerifyDecodedImage(img); + Assert.IsType(format); + } + } + [Fact] + public void WhenFileNotFound_Throws() + { + System.IO.FileNotFoundException ex = Assert.Throws( + () => + { + Image.Load(Guid.NewGuid().ToString()); + }); + } + + [Fact] + public void WhenPathIsNull_Throws() + { + ArgumentNullException ex = Assert.Throws( + () => + { + Image.Load((string)null); + }); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_UseGlobalConfiguration.cs b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_UseGlobalConfiguration.cs index 23c25d9dbb..67b19a0867 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_UseGlobalConfiguration.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_UseGlobalConfiguration.cs @@ -17,13 +17,13 @@ namespace SixLabors.ImageSharp.Tests { public class Load_FromBytes_UseGlobalConfiguration { - private static byte[] ByteArray { get; } = TestFile.Create(TestImages.Bmp.F).Bytes; + private static byte[] ByteArray { get; } = TestFile.Create(TestImages.Bmp.Bit8).Bytes; private static Span ByteSpan => new Span(ByteArray); private static void VerifyDecodedImage(Image img) { - Assert.Equal(new Size(108, 202), img.Size()); + Assert.Equal(new Size(127, 64), img.Size()); } [Theory] diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_UseDefaultConfiguration.cs b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_UseDefaultConfiguration.cs index 27aba42ead..980ed17ceb 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_UseDefaultConfiguration.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_UseDefaultConfiguration.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; using System.IO; using SixLabors.ImageSharp.Formats; @@ -14,42 +15,41 @@ namespace SixLabors.ImageSharp.Tests { public partial class ImageTests { - public class Load_FromStream_UseDefaultConfiguration + public class Load_FromStream_UseDefaultConfiguration : IDisposable { + private static readonly byte[] Data = TestFile.Create(TestImages.Bmp.Bit8).Bytes; + + private MemoryStream Stream { get; } = new MemoryStream(Data); + + private static void VerifyDecodedImage(Image img) + { + Assert.Equal(new Size(127, 64), img.Size()); + } + [Fact] public void Stream_Specific() { - byte[] data = TestFile.Create(TestImages.Bmp.F).Bytes; - - using (var stream = new MemoryStream(data)) - using (var img = Image.Load(stream)) + using (var img = Image.Load(this.Stream)) { - Assert.Equal(new Size(108, 202), img.Size()); + VerifyDecodedImage(img); } } [Fact] public void Stream_Agnostic() { - byte[] data = TestFile.Create(TestImages.Bmp.F).Bytes; - - using (var stream = new MemoryStream(data)) - using (var img = Image.Load(stream)) + using (var img = Image.Load(this.Stream)) { - Assert.Equal(new Size(108, 202), img.Size()); + VerifyDecodedImage(img); } } [Fact] public void Stream_OutFormat_Specific() { - byte[] data = TestFile.Create(TestImages.Bmp.F).Bytes; - - - using (var stream = new MemoryStream(data)) - using (var img = Image.Load(stream, out IImageFormat format)) + using (var img = Image.Load(this.Stream, out IImageFormat format)) { - Assert.Equal(new Size(108, 202), img.Size()); + VerifyDecodedImage(img); Assert.IsType(format); } } @@ -57,39 +57,35 @@ namespace SixLabors.ImageSharp.Tests [Fact] public void Stream_Decoder_Specific() { - byte[] data = TestFile.Create(TestImages.Bmp.F).Bytes; - - using (var stream = new MemoryStream(data)) - using (var img = Image.Load(stream, new BmpDecoder())) + using (var img = Image.Load(this.Stream, new BmpDecoder())) { - Assert.Equal(new Size(108, 202), img.Size()); + VerifyDecodedImage(img); } } [Fact] public void Stream_Decoder_Agnostic() { - byte[] data = TestFile.Create(TestImages.Bmp.F).Bytes; - - using (var stream = new MemoryStream(data)) - using (var img = Image.Load(stream, new BmpDecoder())) + using (var img = Image.Load(this.Stream, new BmpDecoder())) { - Assert.Equal(new Size(108, 202), img.Size()); + VerifyDecodedImage(img); } } [Fact] public void Stream_OutFormat_Agnostic() { - byte[] data = TestFile.Create(TestImages.Bmp.F).Bytes; - - using (var stream = new MemoryStream(data)) - using (var img = Image.Load(stream, out IImageFormat format)) + using (var img = Image.Load(this.Stream, out IImageFormat format)) { - Assert.Equal(new Size(108, 202), img.Size()); + VerifyDecodedImage(img); Assert.IsType(format); } } + + public void Dispose() + { + this.Stream?.Dispose(); + } } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index efdf89964c..0b727f30ce 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -1,7 +1,6 @@  - - netcoreapp2.1 + netcoreapp2.1;net462;net472 True latest full From 3998b6d895c01551356979689320c3ad458546ed Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 5 May 2019 21:32:54 +0200 Subject: [PATCH 136/223] fix changes applied by mistake --- src/ImageSharp.Drawing/ImageSharp.Drawing.csproj | 2 +- src/ImageSharp/ImageSharp.csproj | 2 +- .../Processing/Processors/Dithering/DitherTests.cs | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj index ac5d7fdea6..915915f6cc 100644 --- a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj +++ b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj @@ -10,8 +10,8 @@ $(packageversion) 0.0.1 + netcoreapp2.1;netstandard1.3;netstandard2.0 - netstandard2.0 7.3 true diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index e763b8ef1c..d734648f41 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -10,7 +10,7 @@ $(packageversion) 0.0.1 - + netcoreapp2.1;netstandard1.3;netstandard2.0;net472 true diff --git a/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs index 91422faaa1..cb901c2a88 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs @@ -50,7 +50,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization /// The output is visually correct old 32bit runtime, /// but it is very different because of floating point inaccuracies. /// - private static readonly bool SkipAllDitherTests = false; + private static readonly bool SkipAllDitherTests = + !TestEnvironment.Is64BitProcess && string.IsNullOrEmpty(TestEnvironment.NetCoreVersion); [Theory] [WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Rgba32)] From f4e497de5a322efe9db633c4ec3f1780739b8249 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 5 May 2019 22:13:29 +0200 Subject: [PATCH 137/223] Fix docs for processing extensions --- .../Processing/AutoOrientExtensions.cs | 3 ++- .../Processing/BackgroundColorExtensions.cs | 3 ++- .../Processing/BinaryDiffuseExtensions.cs | 11 ++++---- .../Processing/BinaryDitherExtensions.cs | 11 ++++---- .../Processing/BinaryThresholdExtensions.cs | 11 ++++---- .../Processing/BlackWhiteExtensions.cs | 7 ++--- .../Processing/BoxBlurExtensions.cs | 9 ++++--- .../Processing/BrightnessExtensions.cs | 7 ++--- .../Processing/ColorBlindnessExtensions.cs | 8 +++--- .../Processing/ContrastExtensions.cs | 8 +++--- src/ImageSharp/Processing/CropExtensions.cs | 7 ++--- .../Processing/DetectEdgesExtensions.cs | 16 +++++------ .../Processing/DiffuseExtensions.cs | 15 ++++++----- src/ImageSharp/Processing/DitherExtensions.cs | 13 ++++----- .../Processing/EntropyCropExtensions.cs | 7 ++--- src/ImageSharp/Processing/FilterExtensions.cs | 7 ++--- src/ImageSharp/Processing/FlipExtensions.cs | 5 ++-- .../Processing/GaussianBlurExtensions.cs | 9 ++++--- .../Processing/GaussianSharpenExtensions.cs | 9 ++++--- src/ImageSharp/Processing/GlowExtensions.cs | 27 ++++++++++--------- .../Processing/GrayscaleExtensions.cs | 3 ++- .../HistogramEqualizationExtension.cs | 6 ++--- src/ImageSharp/Processing/HueExtensions.cs | 7 ++--- src/ImageSharp/Processing/InvertExtensions.cs | 7 ++--- .../Processing/KodachromeExtensions.cs | 7 ++--- .../Processing/LomographExtensions.cs | 7 ++--- .../Processing/OilPaintExtensions.cs | 11 ++++---- .../Processing/OpacityExtensions.cs | 7 ++--- src/ImageSharp/Processing/PadExtensions.cs | 5 ++-- .../Processing/PixelateExtensions.cs | 9 ++++--- .../Processing/PolaroidExtensions.cs | 7 ++--- .../Processing/QuantizeExtensions.cs | 7 ++--- src/ImageSharp/Processing/ResizeExtensions.cs | 23 ++++++++-------- src/ImageSharp/Processing/RotateExtensions.cs | 9 ++++--- .../Processing/RotateFlipExtensions.cs | 5 ++-- .../Processing/SaturateExtensions.cs | 7 ++--- src/ImageSharp/Processing/SepiaExtensions.cs | 11 ++++---- src/ImageSharp/Processing/SkewExtensions.cs | 7 ++--- .../Processing/TransformExtensions.cs | 17 ++++++------ .../Processing/VignetteExtensions.cs | 23 ++++++++-------- 40 files changed, 207 insertions(+), 171 deletions(-) diff --git a/src/ImageSharp/Processing/AutoOrientExtensions.cs b/src/ImageSharp/Processing/AutoOrientExtensions.cs index d11fc96237..d065d4583d 100644 --- a/src/ImageSharp/Processing/AutoOrientExtensions.cs +++ b/src/ImageSharp/Processing/AutoOrientExtensions.cs @@ -7,7 +7,8 @@ using SixLabors.ImageSharp.Processing.Processors.Transforms; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of auto-orientation operations to the type. + /// Defines extensions that allow the application of auto-orientation operations to an + /// using Mutate/Clone. /// public static class AutoOrientExtensions { diff --git a/src/ImageSharp/Processing/BackgroundColorExtensions.cs b/src/ImageSharp/Processing/BackgroundColorExtensions.cs index 1ad2c92371..3b794e3351 100644 --- a/src/ImageSharp/Processing/BackgroundColorExtensions.cs +++ b/src/ImageSharp/Processing/BackgroundColorExtensions.cs @@ -8,7 +8,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of a background color to the type. + /// Defines extension methods to replace the background color of an + /// using Mutate/Clone. /// public static class BackgroundColorExtensions { diff --git a/src/ImageSharp/Processing/BinaryDiffuseExtensions.cs b/src/ImageSharp/Processing/BinaryDiffuseExtensions.cs index 788942dde4..487b64e1c4 100644 --- a/src/ImageSharp/Processing/BinaryDiffuseExtensions.cs +++ b/src/ImageSharp/Processing/BinaryDiffuseExtensions.cs @@ -9,7 +9,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds binary diffusion extensions to the type. + /// Defines extension methods to apply binary diffusion on an + /// using Mutate/Clone. /// public static class BinaryDiffuseExtensions { @@ -20,7 +21,7 @@ namespace SixLabors.ImageSharp.Processing /// The image this method extends. /// The diffusion algorithm to apply. /// The threshold to apply binarization of the image. Must be between 0 and 1. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext BinaryDiffuse(this IImageProcessingContext source, IErrorDiffuser diffuser, float threshold) where TPixel : struct, IPixel => source.ApplyProcessor(new BinaryErrorDiffusionProcessor(diffuser, threshold)); @@ -35,7 +36,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext BinaryDiffuse(this IImageProcessingContext source, IErrorDiffuser diffuser, float threshold, Rectangle rectangle) where TPixel : struct, IPixel => source.ApplyProcessor(new BinaryErrorDiffusionProcessor(diffuser, threshold), rectangle); @@ -49,7 +50,7 @@ namespace SixLabors.ImageSharp.Processing /// The threshold to apply binarization of the image. Must be between 0 and 1. /// The color to use for pixels that are above the threshold. /// The color to use for pixels that are below the threshold - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext BinaryDiffuse(this IImageProcessingContext source, IErrorDiffuser diffuser, float threshold, TPixel upperColor, TPixel lowerColor) where TPixel : struct, IPixel => source.ApplyProcessor(new BinaryErrorDiffusionProcessor(diffuser, threshold, upperColor, lowerColor)); @@ -66,7 +67,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext BinaryDiffuse(this IImageProcessingContext source, IErrorDiffuser diffuser, float threshold, TPixel upperColor, TPixel lowerColor, Rectangle rectangle) where TPixel : struct, IPixel => source.ApplyProcessor(new BinaryErrorDiffusionProcessor(diffuser, threshold, upperColor, lowerColor), rectangle); diff --git a/src/ImageSharp/Processing/BinaryDitherExtensions.cs b/src/ImageSharp/Processing/BinaryDitherExtensions.cs index 6177701964..d8843dafac 100644 --- a/src/ImageSharp/Processing/BinaryDitherExtensions.cs +++ b/src/ImageSharp/Processing/BinaryDitherExtensions.cs @@ -9,7 +9,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds binary dithering extensions to the type. + /// Defines extensions to apply binary dithering on an + /// using Mutate/Clone. /// public static class BinaryDitherExtensions { @@ -19,7 +20,7 @@ namespace SixLabors.ImageSharp.Processing /// The pixel format. /// The image this method extends. /// The ordered ditherer. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext BinaryDither(this IImageProcessingContext source, IOrderedDither dither) where TPixel : struct, IPixel => source.ApplyProcessor(new BinaryOrderedDitherProcessor(dither)); @@ -32,7 +33,7 @@ namespace SixLabors.ImageSharp.Processing /// The ordered ditherer. /// The color to use for pixels that are above the threshold. /// The color to use for pixels that are below the threshold - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext BinaryDither(this IImageProcessingContext source, IOrderedDither dither, TPixel upperColor, TPixel lowerColor) where TPixel : struct, IPixel => source.ApplyProcessor(new BinaryOrderedDitherProcessor(dither, upperColor, lowerColor)); @@ -46,7 +47,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext BinaryDither(this IImageProcessingContext source, IOrderedDither dither, Rectangle rectangle) where TPixel : struct, IPixel => source.ApplyProcessor(new BinaryOrderedDitherProcessor(dither), rectangle); @@ -62,7 +63,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext BinaryDither(this IImageProcessingContext source, IOrderedDither dither, TPixel upperColor, TPixel lowerColor, Rectangle rectangle) where TPixel : struct, IPixel => source.ApplyProcessor(new BinaryOrderedDitherProcessor(dither, upperColor, lowerColor), rectangle); diff --git a/src/ImageSharp/Processing/BinaryThresholdExtensions.cs b/src/ImageSharp/Processing/BinaryThresholdExtensions.cs index 31f81ba4b1..aecb784848 100644 --- a/src/ImageSharp/Processing/BinaryThresholdExtensions.cs +++ b/src/ImageSharp/Processing/BinaryThresholdExtensions.cs @@ -8,7 +8,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds binary thresholding extensions to the type. + /// Defines extension methods to apply binary thresholding on an + /// using Mutate/Clone. /// public static class BinaryThresholdExtensions { @@ -18,7 +19,7 @@ namespace SixLabors.ImageSharp.Processing /// The pixel format. /// The image this method extends. /// The threshold to apply binarization of the image. Must be between 0 and 1. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext BinaryThreshold(this IImageProcessingContext source, float threshold) where TPixel : struct, IPixel => source.ApplyProcessor(new BinaryThresholdProcessor(threshold)); @@ -32,7 +33,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext BinaryThreshold(this IImageProcessingContext source, float threshold, Rectangle rectangle) where TPixel : struct, IPixel => source.ApplyProcessor(new BinaryThresholdProcessor(threshold), rectangle); @@ -45,7 +46,7 @@ namespace SixLabors.ImageSharp.Processing /// The threshold to apply binarization of the image. Must be between 0 and 1. /// The color to use for pixels that are above the threshold. /// The color to use for pixels that are below the threshold - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext BinaryThreshold(this IImageProcessingContext source, float threshold, TPixel upperColor, TPixel lowerColor) where TPixel : struct, IPixel => source.ApplyProcessor(new BinaryThresholdProcessor(threshold, upperColor, lowerColor)); @@ -61,7 +62,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext BinaryThreshold(this IImageProcessingContext source, float threshold, TPixel upperColor, TPixel lowerColor, Rectangle rectangle) where TPixel : struct, IPixel => source.ApplyProcessor(new BinaryThresholdProcessor(threshold, upperColor, lowerColor), rectangle); diff --git a/src/ImageSharp/Processing/BlackWhiteExtensions.cs b/src/ImageSharp/Processing/BlackWhiteExtensions.cs index 5dc2341e57..ee34cd99e2 100644 --- a/src/ImageSharp/Processing/BlackWhiteExtensions.cs +++ b/src/ImageSharp/Processing/BlackWhiteExtensions.cs @@ -8,7 +8,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of black and white toning to the type. + /// Defines extension methods that allow the application of black and white toning to an + /// using Mutate/Clone. /// public static class BlackWhiteExtensions { @@ -16,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing /// Applies black and white toning to the image. /// /// The image this method extends. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext BlackWhite(this IImageProcessingContext source) => source.ApplyProcessor(new BlackWhiteProcessor()); @@ -27,7 +28,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext BlackWhite(this IImageProcessingContext source, Rectangle rectangle) => source.ApplyProcessor(new BlackWhiteProcessor(), rectangle); } diff --git a/src/ImageSharp/Processing/BoxBlurExtensions.cs b/src/ImageSharp/Processing/BoxBlurExtensions.cs index bc6e06dd67..f3400c24e6 100644 --- a/src/ImageSharp/Processing/BoxBlurExtensions.cs +++ b/src/ImageSharp/Processing/BoxBlurExtensions.cs @@ -8,7 +8,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds box blurring extensions to the type. + /// Defines extensions methods to apply box blurring to an + /// using Mutate/Clone. /// public static class BoxBlurExtensions { @@ -16,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing /// Applies a box blur to the image. /// /// The image this method extends. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext BoxBlur(this IImageProcessingContext source) => source.ApplyProcessor(new BoxBlurProcessor()); @@ -25,7 +26,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image this method extends. /// The 'radius' value representing the size of the area to sample. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext BoxBlur(this IImageProcessingContext source, int radius) => source.ApplyProcessor(new BoxBlurProcessor(radius)); @@ -37,7 +38,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext BoxBlur(this IImageProcessingContext source, int radius, Rectangle rectangle) => source.ApplyProcessor(new BoxBlurProcessor(radius), rectangle); } diff --git a/src/ImageSharp/Processing/BrightnessExtensions.cs b/src/ImageSharp/Processing/BrightnessExtensions.cs index 4de4dc897b..db84091763 100644 --- a/src/ImageSharp/Processing/BrightnessExtensions.cs +++ b/src/ImageSharp/Processing/BrightnessExtensions.cs @@ -8,7 +8,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the alteration of the brightness component to the type. + /// Defines extensions that allow the alteration of the brightness component of an + /// using Mutate/Clone. /// public static class BrightnessExtensions { @@ -21,7 +22,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image this method extends. /// The proportion of the conversion. Must be greater than or equal to 0. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Brightness(this IImageProcessingContext source, float amount) => source.ApplyProcessor(new BrightnessProcessor(amount)); @@ -37,7 +38,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Brightness(this IImageProcessingContext source, float amount, Rectangle rectangle) => source.ApplyProcessor(new BrightnessProcessor(amount), rectangle); } diff --git a/src/ImageSharp/Processing/ColorBlindnessExtensions.cs b/src/ImageSharp/Processing/ColorBlindnessExtensions.cs index 000d47bd3e..b8d503955e 100644 --- a/src/ImageSharp/Processing/ColorBlindnessExtensions.cs +++ b/src/ImageSharp/Processing/ColorBlindnessExtensions.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors; using SixLabors.ImageSharp.Processing.Processors.Filters; using SixLabors.Primitives; @@ -9,7 +8,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that simulate the effects of various color blindness disorders to the type. + /// Defines extensions that simulate the effects of various color blindness disorders on an + /// using Mutate/Clone. /// public static class ColorBlindnessExtensions { @@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image this method extends. /// The type of color blindness simulator to apply. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext ColorBlindness(this IImageProcessingContext source, ColorBlindnessMode colorBlindness) => source.ApplyProcessor(GetProcessor(colorBlindness)); @@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext ColorBlindness(this IImageProcessingContext source, ColorBlindnessMode colorBlindnessMode, Rectangle rectangle) => source.ApplyProcessor(GetProcessor(colorBlindnessMode), rectangle); diff --git a/src/ImageSharp/Processing/ContrastExtensions.cs b/src/ImageSharp/Processing/ContrastExtensions.cs index b03844d93b..bdfd7c98a4 100644 --- a/src/ImageSharp/Processing/ContrastExtensions.cs +++ b/src/ImageSharp/Processing/ContrastExtensions.cs @@ -1,14 +1,14 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Filters; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the alteration of the contrast component to the type. + /// Defines extensions that allow the alteration of the contrast component of an + /// using Mutate/Clone. /// public static class ContrastExtensions { @@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image this method extends. /// The proportion of the conversion. Must be greater than or equal to 0. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Contrast(this IImageProcessingContext source, float amount) => source.ApplyProcessor(new ContrastProcessor(amount)); @@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Contrast(this IImageProcessingContext source, float amount, Rectangle rectangle) => source.ApplyProcessor(new ContrastProcessor(amount), rectangle); } diff --git a/src/ImageSharp/Processing/CropExtensions.cs b/src/ImageSharp/Processing/CropExtensions.cs index 6aaff5656f..7ec85169e2 100644 --- a/src/ImageSharp/Processing/CropExtensions.cs +++ b/src/ImageSharp/Processing/CropExtensions.cs @@ -7,7 +7,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of cropping operations to the type. + /// Defines extensions that allow the application of cropping operations on an + /// using Mutate/Clone. /// public static class CropExtensions { @@ -17,7 +18,7 @@ namespace SixLabors.ImageSharp.Processing /// The image to resize. /// The target image width. /// The target image height. - /// The + /// The to allow chaining of operations. public static IImageProcessingContext Crop(this IImageProcessingContext source, int width, int height) => Crop(source, new Rectangle(0, 0, width, height)); @@ -28,7 +29,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to retain. /// - /// The + /// The to allow chaining of operations. public static IImageProcessingContext Crop(this IImageProcessingContext source, Rectangle cropRectangle) => source.ApplyProcessor(new CropProcessor(cropRectangle, source.GetCurrentSize())); } diff --git a/src/ImageSharp/Processing/DetectEdgesExtensions.cs b/src/ImageSharp/Processing/DetectEdgesExtensions.cs index 7527d601df..837b26910d 100644 --- a/src/ImageSharp/Processing/DetectEdgesExtensions.cs +++ b/src/ImageSharp/Processing/DetectEdgesExtensions.cs @@ -8,7 +8,7 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds edge detection extensions to the type. + /// Defines edge detection extensions applicable on an using Mutate/Clone. /// public static class DetectEdgesExtensions { @@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing /// operating in grayscale mode. /// /// The image this method extends. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext DetectEdges(this IImageProcessingContext source) => DetectEdges(source, new SobelProcessor(true)); @@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, Rectangle rectangle) => DetectEdges(source, rectangle, new SobelProcessor(true)); @@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image this method extends. /// The filter for detecting edges. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext DetectEdges( this IImageProcessingContext source, EdgeDetectionOperators filter) => @@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.Processing /// The image this method extends. /// The filter for detecting edges. /// Whether to convert the image to grayscale first. Defaults to true. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext DetectEdges( this IImageProcessingContext source, EdgeDetectionOperators filter, @@ -66,7 +66,7 @@ namespace SixLabors.ImageSharp.Processing /// The structure that specifies the portion of the image object to alter. /// /// Whether to convert the image to grayscale first. Defaults to true. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext DetectEdges( this IImageProcessingContext source, EdgeDetectionOperators filter, @@ -79,7 +79,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image this method extends. /// The filter for detecting edges. - /// The . + /// The to allow chaining of operations. private static IImageProcessingContext DetectEdges(this IImageProcessingContext source, IImageProcessor filter) { return source.ApplyProcessor(filter); @@ -93,7 +93,7 @@ namespace SixLabors.ImageSharp.Processing /// The structure that specifies the portion of the image object to alter. /// /// The filter for detecting edges. - /// The . + /// The to allow chaining of operations. private static IImageProcessingContext DetectEdges( this IImageProcessingContext source, Rectangle rectangle, diff --git a/src/ImageSharp/Processing/DiffuseExtensions.cs b/src/ImageSharp/Processing/DiffuseExtensions.cs index 768d28116b..4668363e9a 100644 --- a/src/ImageSharp/Processing/DiffuseExtensions.cs +++ b/src/ImageSharp/Processing/DiffuseExtensions.cs @@ -8,7 +8,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Dithering { /// - /// Adds diffusion extensions to the type. + /// Defines extension methods to apply diffusion to an + /// using Mutate/Clone. /// public static class DiffuseExtensions { @@ -17,7 +18,7 @@ namespace SixLabors.ImageSharp.Processing.Dithering /// /// The pixel format. /// The image this method extends. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Diffuse(this IImageProcessingContext source) where TPixel : struct, IPixel => Diffuse(source, KnownDiffusers.FloydSteinberg, .5F); @@ -28,7 +29,7 @@ namespace SixLabors.ImageSharp.Processing.Dithering /// The pixel format. /// The image this method extends. /// The threshold to apply binarization of the image. Must be between 0 and 1. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Diffuse(this IImageProcessingContext source, float threshold) where TPixel : struct, IPixel => Diffuse(source, KnownDiffusers.FloydSteinberg, threshold); @@ -40,7 +41,7 @@ namespace SixLabors.ImageSharp.Processing.Dithering /// The image this method extends. /// The diffusion algorithm to apply. /// The threshold to apply binarization of the image. Must be between 0 and 1. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Diffuse(this IImageProcessingContext source, IErrorDiffuser diffuser, float threshold) where TPixel : struct, IPixel => source.ApplyProcessor(new ErrorDiffusionPaletteProcessor(diffuser, threshold)); @@ -55,7 +56,7 @@ namespace SixLabors.ImageSharp.Processing.Dithering /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Diffuse(this IImageProcessingContext source, IErrorDiffuser diffuser, float threshold, Rectangle rectangle) where TPixel : struct, IPixel => source.ApplyProcessor(new ErrorDiffusionPaletteProcessor(diffuser, threshold), rectangle); @@ -68,7 +69,7 @@ namespace SixLabors.ImageSharp.Processing.Dithering /// The diffusion algorithm to apply. /// The threshold to apply binarization of the image. Must be between 0 and 1. /// The palette to select substitute colors from. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Diffuse(this IImageProcessingContext source, IErrorDiffuser diffuser, float threshold, TPixel[] palette) where TPixel : struct, IPixel => source.ApplyProcessor(new ErrorDiffusionPaletteProcessor(diffuser, threshold, palette)); @@ -84,7 +85,7 @@ namespace SixLabors.ImageSharp.Processing.Dithering /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Diffuse(this IImageProcessingContext source, IErrorDiffuser diffuser, float threshold, TPixel[] palette, Rectangle rectangle) where TPixel : struct, IPixel => source.ApplyProcessor(new ErrorDiffusionPaletteProcessor(diffuser, threshold, palette), rectangle); diff --git a/src/ImageSharp/Processing/DitherExtensions.cs b/src/ImageSharp/Processing/DitherExtensions.cs index 795561e702..aeb975d1c1 100644 --- a/src/ImageSharp/Processing/DitherExtensions.cs +++ b/src/ImageSharp/Processing/DitherExtensions.cs @@ -8,7 +8,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds dithering extensions to the type. + /// Defines dithering extensions to apply on an + /// using Mutate/Clone. /// public static class DitherExtensions { @@ -17,7 +18,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The pixel format. /// The image this method extends. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Dither(this IImageProcessingContext source) where TPixel : struct, IPixel => Dither(source, KnownDitherers.BayerDither4x4); @@ -28,7 +29,7 @@ namespace SixLabors.ImageSharp.Processing /// The pixel format. /// The image this method extends. /// The ordered ditherer. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Dither(this IImageProcessingContext source, IOrderedDither dither) where TPixel : struct, IPixel => source.ApplyProcessor(new OrderedDitherPaletteProcessor(dither)); @@ -40,7 +41,7 @@ namespace SixLabors.ImageSharp.Processing /// The image this method extends. /// The ordered ditherer. /// The palette to select substitute colors from. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Dither(this IImageProcessingContext source, IOrderedDither dither, TPixel[] palette) where TPixel : struct, IPixel => source.ApplyProcessor(new OrderedDitherPaletteProcessor(dither, palette)); @@ -54,7 +55,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Dither(this IImageProcessingContext source, IOrderedDither dither, Rectangle rectangle) where TPixel : struct, IPixel => source.ApplyProcessor(new OrderedDitherPaletteProcessor(dither), rectangle); @@ -69,7 +70,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Dither(this IImageProcessingContext source, IOrderedDither dither, TPixel[] palette, Rectangle rectangle) where TPixel : struct, IPixel => source.ApplyProcessor(new OrderedDitherPaletteProcessor(dither, palette), rectangle); diff --git a/src/ImageSharp/Processing/EntropyCropExtensions.cs b/src/ImageSharp/Processing/EntropyCropExtensions.cs index 34bc5daebf..de5296d83e 100644 --- a/src/ImageSharp/Processing/EntropyCropExtensions.cs +++ b/src/ImageSharp/Processing/EntropyCropExtensions.cs @@ -6,7 +6,8 @@ using SixLabors.ImageSharp.Processing.Processors.Transforms; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of entropy cropping operations to the type. + /// Defines extensions that allow the application of entropy cropping operations on an + /// using Mutate/Clone. /// public static class EntropyCropExtensions { @@ -14,7 +15,7 @@ namespace SixLabors.ImageSharp.Processing /// Crops an image to the area of greatest entropy using a threshold for entropic density of .5F. /// /// The image to crop. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext EntropyCrop(this IImageProcessingContext source) => source.ApplyProcessor(new EntropyCropProcessor()); @@ -23,7 +24,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image to crop. /// The threshold for entropic density. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext EntropyCrop(this IImageProcessingContext source, float threshold) => source.ApplyProcessor(new EntropyCropProcessor(threshold)); } diff --git a/src/ImageSharp/Processing/FilterExtensions.cs b/src/ImageSharp/Processing/FilterExtensions.cs index 2a1c6bc7f9..5a66502ce5 100644 --- a/src/ImageSharp/Processing/FilterExtensions.cs +++ b/src/ImageSharp/Processing/FilterExtensions.cs @@ -9,7 +9,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of composable filters to the type. + /// Defines extensions that allow the application of composable filters to an + /// using Mutate/Clone. /// public static class FilterExtensions { @@ -18,7 +19,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image this method extends. /// The filter color matrix - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Filter(this IImageProcessingContext source, ColorMatrix matrix) => source.ApplyProcessor(new FilterProcessor(matrix)); @@ -30,7 +31,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Filter(this IImageProcessingContext source, ColorMatrix matrix, Rectangle rectangle) => source.ApplyProcessor(new FilterProcessor(matrix), rectangle); } diff --git a/src/ImageSharp/Processing/FlipExtensions.cs b/src/ImageSharp/Processing/FlipExtensions.cs index bc862972e5..f6b3c0c374 100644 --- a/src/ImageSharp/Processing/FlipExtensions.cs +++ b/src/ImageSharp/Processing/FlipExtensions.cs @@ -6,7 +6,8 @@ using SixLabors.ImageSharp.Processing.Processors.Transforms; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of flipping operations to the type. + /// Defines extensions that allow the application of flipping operations on an + /// using Mutate/Clone. /// public static class FlipExtensions { @@ -15,7 +16,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image to rotate, flip, or both. /// The to perform the flip. - /// The + /// The to allow chaining of operations. public static IImageProcessingContext Flip(this IImageProcessingContext source, FlipMode flipMode) => source.ApplyProcessor(new FlipProcessor(flipMode)); } diff --git a/src/ImageSharp/Processing/GaussianBlurExtensions.cs b/src/ImageSharp/Processing/GaussianBlurExtensions.cs index 1dab054579..e527a14b73 100644 --- a/src/ImageSharp/Processing/GaussianBlurExtensions.cs +++ b/src/ImageSharp/Processing/GaussianBlurExtensions.cs @@ -8,7 +8,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds Gaussian blurring extensions to the type. + /// Defines Gaussian blurring extensions to apply on an + /// using Mutate/Clone. /// public static class GaussianBlurExtensions { @@ -16,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing /// Applies a Gaussian blur to the image. /// /// The image this method extends. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source) => source.ApplyProcessor(new GaussianBlurProcessor()); @@ -25,7 +26,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image this method extends. /// The 'sigma' value representing the weight of the blur. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source, float sigma) => source.ApplyProcessor(new GaussianBlurProcessor(sigma)); @@ -37,7 +38,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source, float sigma, Rectangle rectangle) => source.ApplyProcessor(new GaussianBlurProcessor(sigma), rectangle); } diff --git a/src/ImageSharp/Processing/GaussianSharpenExtensions.cs b/src/ImageSharp/Processing/GaussianSharpenExtensions.cs index 64aa7b5db3..79f4a0cc30 100644 --- a/src/ImageSharp/Processing/GaussianSharpenExtensions.cs +++ b/src/ImageSharp/Processing/GaussianSharpenExtensions.cs @@ -7,7 +7,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds Gaussian sharpening extensions to the type. + /// Defines Gaussian sharpening extensions to apply on an + /// using Mutate/Clone. /// public static class GaussianSharpenExtensions { @@ -15,7 +16,7 @@ namespace SixLabors.ImageSharp.Processing /// Applies a Gaussian sharpening filter to the image. /// /// The image this method extends. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source) => source.ApplyProcessor(new GaussianSharpenProcessor()); @@ -24,7 +25,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image this method extends. /// The 'sigma' value representing the weight of the blur. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source, float sigma) => source.ApplyProcessor(new GaussianSharpenProcessor(sigma)); @@ -36,7 +37,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext GaussianSharpen( this IImageProcessingContext source, float sigma, diff --git a/src/ImageSharp/Processing/GlowExtensions.cs b/src/ImageSharp/Processing/GlowExtensions.cs index 8b6e8ffc22..759fdccbea 100644 --- a/src/ImageSharp/Processing/GlowExtensions.cs +++ b/src/ImageSharp/Processing/GlowExtensions.cs @@ -9,7 +9,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of a radial glow to the type. + /// Defines extensions that allow the application of a radial glow on an + /// using Mutate/Clone. /// public static class GlowExtensions { @@ -18,7 +19,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The pixel format. /// The image this method extends. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Glow(this IImageProcessingContext source) where TPixel : struct, IPixel => Glow(source, GraphicsOptions.Default); @@ -29,7 +30,7 @@ namespace SixLabors.ImageSharp.Processing /// The pixel format. /// The image this method extends. /// The color to set as the glow. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Glow(this IImageProcessingContext source, TPixel color) where TPixel : struct, IPixel { @@ -42,7 +43,7 @@ namespace SixLabors.ImageSharp.Processing /// The pixel format. /// The image this method extends. /// The the radius. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Glow(this IImageProcessingContext source, float radius) where TPixel : struct, IPixel => Glow(source, GraphicsOptions.Default, radius); @@ -55,7 +56,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Glow(this IImageProcessingContext source, Rectangle rectangle) where TPixel : struct, IPixel => source.Glow(GraphicsOptions.Default, rectangle); @@ -70,7 +71,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Glow(this IImageProcessingContext source, TPixel color, float radius, Rectangle rectangle) where TPixel : struct, IPixel => source.Glow(GraphicsOptions.Default, color, ValueSize.Absolute(radius), rectangle); @@ -81,7 +82,7 @@ namespace SixLabors.ImageSharp.Processing /// The pixel format. /// The image this method extends. /// The options effecting things like blending. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Glow(this IImageProcessingContext source, GraphicsOptions options) where TPixel : struct, IPixel => source.Glow(options, NamedColors.Black, ValueSize.PercentageOfWidth(0.5f)); @@ -93,7 +94,7 @@ namespace SixLabors.ImageSharp.Processing /// The image this method extends. /// The options effecting things like blending. /// The color to set as the glow. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Glow(this IImageProcessingContext source, GraphicsOptions options, TPixel color) where TPixel : struct, IPixel => source.Glow(options, color, ValueSize.PercentageOfWidth(0.5f)); @@ -105,7 +106,7 @@ namespace SixLabors.ImageSharp.Processing /// The image this method extends. /// The options effecting things like blending. /// The the radius. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Glow(this IImageProcessingContext source, GraphicsOptions options, float radius) where TPixel : struct, IPixel => source.Glow(options, NamedColors.Black, ValueSize.Absolute(radius)); @@ -119,7 +120,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Glow(this IImageProcessingContext source, GraphicsOptions options, Rectangle rectangle) where TPixel : struct, IPixel => source.Glow(options, NamedColors.Black, ValueSize.PercentageOfWidth(0.5f), rectangle); @@ -135,7 +136,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Glow(this IImageProcessingContext source, GraphicsOptions options, TPixel color, float radius, Rectangle rectangle) where TPixel : struct, IPixel => source.Glow(options, color, ValueSize.Absolute(radius), rectangle); @@ -151,7 +152,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. private static IImageProcessingContext Glow(this IImageProcessingContext source, GraphicsOptions options, TPixel color, ValueSize radius, Rectangle rectangle) where TPixel : struct, IPixel => source.ApplyProcessor(new GlowProcessor(color, radius, options), rectangle); @@ -164,7 +165,7 @@ namespace SixLabors.ImageSharp.Processing /// The options effecting things like blending. /// The color to set as the glow. /// The the radius. - /// The . + /// The to allow chaining of operations. private static IImageProcessingContext Glow(this IImageProcessingContext source, GraphicsOptions options, TPixel color, ValueSize radius) where TPixel : struct, IPixel => source.ApplyProcessor(new GlowProcessor(color, radius, options)); diff --git a/src/ImageSharp/Processing/GrayscaleExtensions.cs b/src/ImageSharp/Processing/GrayscaleExtensions.cs index d68a385e47..a87341025d 100644 --- a/src/ImageSharp/Processing/GrayscaleExtensions.cs +++ b/src/ImageSharp/Processing/GrayscaleExtensions.cs @@ -9,7 +9,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of grayscale toning to the type. + /// Defines extensions that allow the application of grayscale toning to an + /// using Mutate/Clone. /// public static class GrayscaleExtensions { diff --git a/src/ImageSharp/Processing/HistogramEqualizationExtension.cs b/src/ImageSharp/Processing/HistogramEqualizationExtension.cs index aae3e997fe..01c14fc093 100644 --- a/src/ImageSharp/Processing/HistogramEqualizationExtension.cs +++ b/src/ImageSharp/Processing/HistogramEqualizationExtension.cs @@ -6,7 +6,7 @@ using SixLabors.ImageSharp.Processing.Processors.Normalization; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extension that allow the adjustment of the contrast of an image via its histogram. + /// Defines extension that allow the adjustment of the contrast of an image via its histogram. /// public static class HistogramEqualizationExtension { @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Processing /// Equalizes the histogram of an image to increases the contrast. /// /// The image this method extends. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext HistogramEqualization(this IImageProcessingContext source) => HistogramEqualization(source, HistogramEqualizationOptions.Default); @@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image this method extends. /// The histogram equalization options to use. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext HistogramEqualization( this IImageProcessingContext source, HistogramEqualizationOptions options) => diff --git a/src/ImageSharp/Processing/HueExtensions.cs b/src/ImageSharp/Processing/HueExtensions.cs index 17296e1db5..3c1239da67 100644 --- a/src/ImageSharp/Processing/HueExtensions.cs +++ b/src/ImageSharp/Processing/HueExtensions.cs @@ -8,7 +8,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the alteration of the hue component to the type. + /// Defines extensions that allow the alteration of the hue component of an + /// using Mutate/Clone. /// public static class HueExtensions { @@ -17,7 +18,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image this method extends. /// The rotation angle in degrees to adjust the hue. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Hue(this IImageProcessingContext source, float degrees) => source.ApplyProcessor(new HueProcessor(degrees)); @@ -29,7 +30,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Hue(this IImageProcessingContext source, float degrees, Rectangle rectangle) => source.ApplyProcessor(new HueProcessor(degrees), rectangle); } diff --git a/src/ImageSharp/Processing/InvertExtensions.cs b/src/ImageSharp/Processing/InvertExtensions.cs index 828aaa1bbc..c45f24c2ea 100644 --- a/src/ImageSharp/Processing/InvertExtensions.cs +++ b/src/ImageSharp/Processing/InvertExtensions.cs @@ -8,7 +8,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the inversion of colors to the type. + /// Defines extensions that allow the inversion of colors of an + /// using Mutate/Clone. /// public static class InvertExtensions { @@ -16,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing /// Inverts the colors of the image. /// /// The image this method extends. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Invert(this IImageProcessingContext source) => source.ApplyProcessor(new InvertProcessor(1F)); @@ -27,7 +28,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Invert(this IImageProcessingContext source, Rectangle rectangle) => source.ApplyProcessor(new InvertProcessor(1F), rectangle); } diff --git a/src/ImageSharp/Processing/KodachromeExtensions.cs b/src/ImageSharp/Processing/KodachromeExtensions.cs index 24bd51e016..810094a180 100644 --- a/src/ImageSharp/Processing/KodachromeExtensions.cs +++ b/src/ImageSharp/Processing/KodachromeExtensions.cs @@ -8,7 +8,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the recreation of an old Kodachrome camera effect to the type. + /// Defines extensions that allow the recreation of an old Kodachrome camera effect on an + /// using Mutate/Clone. /// public static class KodachromeExtensions { @@ -16,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing /// Alters the colors of the image recreating an old Kodachrome camera effect. /// /// The image this method extends. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Kodachrome(this IImageProcessingContext source) => source.ApplyProcessor(new KodachromeProcessor()); @@ -27,7 +28,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Kodachrome(this IImageProcessingContext source, Rectangle rectangle) => source.ApplyProcessor(new KodachromeProcessor(), rectangle); } diff --git a/src/ImageSharp/Processing/LomographExtensions.cs b/src/ImageSharp/Processing/LomographExtensions.cs index a3fd3d3b50..dd7ab21ec1 100644 --- a/src/ImageSharp/Processing/LomographExtensions.cs +++ b/src/ImageSharp/Processing/LomographExtensions.cs @@ -8,7 +8,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the recreation of an old Lomograph camera effect to the type. + /// Defines extensions that allow the recreation of an old Lomograph camera effect on an + /// using Mutate/Clone. /// public static class LomographExtensions { @@ -16,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing /// Alters the colors of the image recreating an old Lomograph camera effect. /// /// The image this method extends. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Lomograph(this IImageProcessingContext source) => source.ApplyProcessor(new LomographProcessor()); @@ -27,7 +28,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Lomograph(this IImageProcessingContext source, Rectangle rectangle) => source.ApplyProcessor(new LomographProcessor(), rectangle); } diff --git a/src/ImageSharp/Processing/OilPaintExtensions.cs b/src/ImageSharp/Processing/OilPaintExtensions.cs index ee51b31fab..1aa98c8c1b 100644 --- a/src/ImageSharp/Processing/OilPaintExtensions.cs +++ b/src/ImageSharp/Processing/OilPaintExtensions.cs @@ -7,7 +7,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds oil painting effect extensions to the type. + /// Defines oil painting effect extensions applicable on an + /// using Mutate/Clone. /// public static class OilPaintExtensions { @@ -16,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing /// set to 10 and 15 respectively. /// /// The image this method extends. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext OilPaint(this IImageProcessingContext source) => OilPaint(source, 10, 15); /// @@ -27,7 +28,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext OilPaint(this IImageProcessingContext source, Rectangle rectangle) => OilPaint(source, 10, 15, rectangle); @@ -37,7 +38,7 @@ namespace SixLabors.ImageSharp.Processing /// The image this method extends. /// The number of intensity levels. Higher values result in a broader range of color intensities forming part of the result image. /// The number of neighboring pixels used in calculating each individual pixel value. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext OilPaint(this IImageProcessingContext source, int levels, int brushSize) => source.ApplyProcessor(new OilPaintingProcessor(levels, brushSize)); @@ -51,7 +52,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext OilPaint( this IImageProcessingContext source, int levels, diff --git a/src/ImageSharp/Processing/OpacityExtensions.cs b/src/ImageSharp/Processing/OpacityExtensions.cs index 6d9198648c..ecf6ce783e 100644 --- a/src/ImageSharp/Processing/OpacityExtensions.cs +++ b/src/ImageSharp/Processing/OpacityExtensions.cs @@ -8,7 +8,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the alteration of the opacity component to the type. + /// Defines extensions that allow the alteration of the opacity component of an + /// using Mutate/Clone. /// public static class OpacityExtensions { @@ -17,7 +18,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image this method extends. /// The proportion of the conversion. Must be between 0 and 1. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Opacity(this IImageProcessingContext source, float amount) => source.ApplyProcessor(new OpacityProcessor(amount)); @@ -29,7 +30,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Opacity(this IImageProcessingContext source, float amount, Rectangle rectangle) => source.ApplyProcessor(new OpacityProcessor(amount), rectangle); } diff --git a/src/ImageSharp/Processing/PadExtensions.cs b/src/ImageSharp/Processing/PadExtensions.cs index 0422f7c59d..29f80b43ff 100644 --- a/src/ImageSharp/Processing/PadExtensions.cs +++ b/src/ImageSharp/Processing/PadExtensions.cs @@ -7,7 +7,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of padding operations to the type. + /// Defines extensions that allow the application of padding operations on an + /// using Mutate/Clone. /// public static class PadExtensions { @@ -17,7 +18,7 @@ namespace SixLabors.ImageSharp.Processing /// The source image to pad. /// The new width. /// The new height. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Pad(this IImageProcessingContext source, int width, int height) { var options = new ResizeOptions diff --git a/src/ImageSharp/Processing/PixelateExtensions.cs b/src/ImageSharp/Processing/PixelateExtensions.cs index d9f7f9be50..bf40af91ad 100644 --- a/src/ImageSharp/Processing/PixelateExtensions.cs +++ b/src/ImageSharp/Processing/PixelateExtensions.cs @@ -7,7 +7,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds pixelation effect extensions to the type. + /// Defines pixelation effect extensions applicable on an + /// using Mutate/Clone. /// public static class PixelateExtensions { @@ -15,7 +16,7 @@ namespace SixLabors.ImageSharp.Processing /// Pixelates an image with the given pixel size. /// /// The image this method extends. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Pixelate(this IImageProcessingContext source) => Pixelate(source, 4); /// @@ -23,7 +24,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image this method extends. /// The size of the pixels. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Pixelate(this IImageProcessingContext source, int size) => source.ApplyProcessor(new PixelateProcessor(size)); @@ -35,7 +36,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Pixelate( this IImageProcessingContext source, int size, diff --git a/src/ImageSharp/Processing/PolaroidExtensions.cs b/src/ImageSharp/Processing/PolaroidExtensions.cs index 4e78411689..eace463579 100644 --- a/src/ImageSharp/Processing/PolaroidExtensions.cs +++ b/src/ImageSharp/Processing/PolaroidExtensions.cs @@ -8,7 +8,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the recreation of an old Polaroid camera effect to the type. + /// Defines extensions that allow the recreation of an old Polaroid camera effect on an + /// using Mutate/Clone. /// public static class PolaroidExtensions { @@ -16,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing /// Alters the colors of the image recreating an old Polaroid camera effect. /// /// The image this method extends. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Polaroid(this IImageProcessingContext source) => source.ApplyProcessor(new PolaroidProcessor()); @@ -27,7 +28,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Polaroid(this IImageProcessingContext source, Rectangle rectangle) => source.ApplyProcessor(new PolaroidProcessor(), rectangle); } diff --git a/src/ImageSharp/Processing/QuantizeExtensions.cs b/src/ImageSharp/Processing/QuantizeExtensions.cs index 5bd2f49bd4..ad351afa99 100644 --- a/src/ImageSharp/Processing/QuantizeExtensions.cs +++ b/src/ImageSharp/Processing/QuantizeExtensions.cs @@ -7,7 +7,8 @@ using SixLabors.ImageSharp.Processing.Processors.Quantization; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of quantizing algorithms to the type. + /// Defines extensions that allow the application of quantizing algorithms on an + /// using Mutate/Clone. /// public static class QuantizeExtensions { @@ -16,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The pixel format. /// The image this method extends. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Quantize(this IImageProcessingContext source) where TPixel : struct, IPixel => Quantize(source, KnownQuantizers.Octree); @@ -27,7 +28,7 @@ namespace SixLabors.ImageSharp.Processing /// The pixel format. /// The image this method extends. /// The quantizer to apply to perform the operation. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Quantize(this IImageProcessingContext source, IQuantizer quantizer) where TPixel : struct, IPixel => source.ApplyProcessor(new QuantizeProcessor(quantizer)); diff --git a/src/ImageSharp/Processing/ResizeExtensions.cs b/src/ImageSharp/Processing/ResizeExtensions.cs index 57dbcb1d16..4578b4353f 100644 --- a/src/ImageSharp/Processing/ResizeExtensions.cs +++ b/src/ImageSharp/Processing/ResizeExtensions.cs @@ -8,7 +8,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of resize operations to the type. + /// Defines extensions that allow the application of resize operations on an + /// using Mutate/Clone. /// public static class ResizeExtensions { @@ -17,7 +18,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image to resize. /// The resize options. - /// The + /// The to allow chaining of operations. /// Passing zero for one of height or width within the resize options will automatically preserve the aspect ratio of the original image or the nearest possible ratio. public static IImageProcessingContext Resize(this IImageProcessingContext source, ResizeOptions options) => source.ApplyProcessor(new ResizeProcessor(options, source.GetCurrentSize())); @@ -27,7 +28,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image to resize. /// The target image size. - /// The + /// The to allow chaining of operations. /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. public static IImageProcessingContext Resize(this IImageProcessingContext source, Size size) => Resize(source, size.Width, size.Height, KnownResamplers.Bicubic, false); @@ -38,7 +39,7 @@ namespace SixLabors.ImageSharp.Processing /// The image to resize. /// The target image size. /// Whether to compress and expand the image color-space to gamma correct the image during processing. - /// The + /// The to allow chaining of operations. /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. public static IImageProcessingContext Resize(this IImageProcessingContext source, Size size, bool compand) => Resize(source, size.Width, size.Height, KnownResamplers.Bicubic, compand); @@ -49,7 +50,7 @@ namespace SixLabors.ImageSharp.Processing /// The image to resize. /// The target image width. /// The target image height. - /// The + /// The to allow chaining of operations. /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height) => Resize(source, width, height, KnownResamplers.Bicubic, false); @@ -61,7 +62,7 @@ namespace SixLabors.ImageSharp.Processing /// The target image width. /// The target image height. /// Whether to compress and expand the image color-space to gamma correct the image during processing. - /// The + /// The to allow chaining of operations. /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, bool compand) => Resize(source, width, height, KnownResamplers.Bicubic, compand); @@ -73,7 +74,7 @@ namespace SixLabors.ImageSharp.Processing /// The target image width. /// The target image height. /// The to perform the resampling. - /// The + /// The to allow chaining of operations. /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, IResampler sampler) => Resize(source, width, height, sampler, false); @@ -85,7 +86,7 @@ namespace SixLabors.ImageSharp.Processing /// The target image size. /// The to perform the resampling. /// Whether to compress and expand the image color-space to gamma correct the image during processing. - /// The + /// The to allow chaining of operations. /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. public static IImageProcessingContext Resize(this IImageProcessingContext source, Size size, IResampler sampler, bool compand) => Resize(source, size.Width, size.Height, sampler, new Rectangle(0, 0, size.Width, size.Height), compand); @@ -98,7 +99,7 @@ namespace SixLabors.ImageSharp.Processing /// The target image height. /// The to perform the resampling. /// Whether to compress and expand the image color-space to gamma correct the image during processing. - /// The + /// The to allow chaining of operations. /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, IResampler sampler, bool compand) => Resize(source, width, height, sampler, new Rectangle(0, 0, width, height), compand); @@ -118,7 +119,7 @@ namespace SixLabors.ImageSharp.Processing /// The structure that specifies the portion of the target image object to draw to. /// /// Whether to compress and expand the image color-space to gamma correct the image during processing. - /// The + /// The to allow chaining of operations. /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. public static IImageProcessingContext Resize( this IImageProcessingContext source, @@ -141,7 +142,7 @@ namespace SixLabors.ImageSharp.Processing /// The structure that specifies the portion of the target image object to draw to. /// /// Whether to compress and expand the image color-space to gamma correct the image during processing. - /// The + /// The to allow chaining of operations. /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. public static IImageProcessingContext Resize( this IImageProcessingContext source, diff --git a/src/ImageSharp/Processing/RotateExtensions.cs b/src/ImageSharp/Processing/RotateExtensions.cs index cb637a1b84..395462ae33 100644 --- a/src/ImageSharp/Processing/RotateExtensions.cs +++ b/src/ImageSharp/Processing/RotateExtensions.cs @@ -6,7 +6,8 @@ using SixLabors.ImageSharp.Processing.Processors.Transforms; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of rotate operations to the type. + /// Defines extensions that allow the application of rotate operations on an + /// using Mutate/Clone. /// public static class RotateExtensions { @@ -15,7 +16,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image to rotate. /// The to perform the rotation. - /// The + /// The to allow chaining of operations. public static IImageProcessingContext Rotate(this IImageProcessingContext source, RotateMode rotateMode) => Rotate(source, (float)rotateMode); @@ -24,7 +25,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image to rotate. /// The angle in degrees to perform the rotation. - /// The + /// The to allow chaining of operations. public static IImageProcessingContext Rotate(this IImageProcessingContext source, float degrees) => Rotate(source, degrees, KnownResamplers.Bicubic); @@ -34,7 +35,7 @@ namespace SixLabors.ImageSharp.Processing /// The image to rotate. /// The angle in degrees to perform the rotation. /// The to perform the resampling. - /// The + /// The to allow chaining of operations. public static IImageProcessingContext Rotate( this IImageProcessingContext source, float degrees, diff --git a/src/ImageSharp/Processing/RotateFlipExtensions.cs b/src/ImageSharp/Processing/RotateFlipExtensions.cs index 5030c942ef..4d5d90c30e 100644 --- a/src/ImageSharp/Processing/RotateFlipExtensions.cs +++ b/src/ImageSharp/Processing/RotateFlipExtensions.cs @@ -6,7 +6,8 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of rotate-flip operations to the type. + /// Defines extensions that allow the application of rotate-flip operations on an + /// using Mutate/Clone. /// public static class RotateFlipExtensions { @@ -16,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing /// The image to rotate, flip, or both. /// The to perform the rotation. /// The to perform the flip. - /// The + /// The to allow chaining of operations. public static IImageProcessingContext RotateFlip(this IImageProcessingContext source, RotateMode rotateMode, FlipMode flipMode) => source.Rotate(rotateMode).Flip(flipMode); } diff --git a/src/ImageSharp/Processing/SaturateExtensions.cs b/src/ImageSharp/Processing/SaturateExtensions.cs index fe2cf5f348..e9ba820b6c 100644 --- a/src/ImageSharp/Processing/SaturateExtensions.cs +++ b/src/ImageSharp/Processing/SaturateExtensions.cs @@ -8,7 +8,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the alteration of the saturation component to the type. + /// Defines extensions that allow the alteration of the saturation component of an + /// using Mutate/Clone. /// public static class SaturateExtensions { @@ -21,7 +22,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image this method extends. /// The proportion of the conversion. Must be greater than or equal to 0. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Saturate(this IImageProcessingContext source, float amount) => source.ApplyProcessor(new SaturateProcessor(amount)); @@ -37,7 +38,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Saturate(this IImageProcessingContext source, float amount, Rectangle rectangle) => source.ApplyProcessor(new SaturateProcessor(amount), rectangle); } diff --git a/src/ImageSharp/Processing/SepiaExtensions.cs b/src/ImageSharp/Processing/SepiaExtensions.cs index 5b23ec11af..5ee5151fae 100644 --- a/src/ImageSharp/Processing/SepiaExtensions.cs +++ b/src/ImageSharp/Processing/SepiaExtensions.cs @@ -8,7 +8,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of sepia toning to the type. + /// Defines extensions that allow the application of sepia toning on an + /// using Mutate/Clone. /// public static class SepiaExtensions { @@ -16,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing /// Applies sepia toning to the image. /// /// The image this method extends. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Sepia(this IImageProcessingContext source) => Sepia(source, 1F); @@ -25,7 +26,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image this method extends. /// The proportion of the conversion. Must be between 0 and 1. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Sepia(this IImageProcessingContext source, float amount) => source.ApplyProcessor(new SepiaProcessor(amount)); @@ -36,7 +37,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Sepia(this IImageProcessingContext source, Rectangle rectangle) => Sepia(source, 1F, rectangle); @@ -48,7 +49,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Sepia(this IImageProcessingContext source, float amount, Rectangle rectangle) => source.ApplyProcessor(new SepiaProcessor(amount), rectangle); } diff --git a/src/ImageSharp/Processing/SkewExtensions.cs b/src/ImageSharp/Processing/SkewExtensions.cs index 7f378d2483..77a46af0d9 100644 --- a/src/ImageSharp/Processing/SkewExtensions.cs +++ b/src/ImageSharp/Processing/SkewExtensions.cs @@ -6,7 +6,8 @@ using SixLabors.ImageSharp.Processing.Processors.Transforms; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of skew operations to the type. + /// Defines extensions that allow the application of skew operations on an + /// using Mutate/Clone. /// public static class SkewExtensions { @@ -16,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing /// The image to skew. /// The angle in degrees to perform the skew along the x-axis. /// The angle in degrees to perform the skew along the y-axis. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Skew(this IImageProcessingContext source, float degreesX, float degreesY) => Skew(source, degreesX, degreesY, KnownResamplers.Bicubic); @@ -28,7 +29,7 @@ namespace SixLabors.ImageSharp.Processing /// The angle in degrees to perform the skew along the x-axis. /// The angle in degrees to perform the skew along the y-axis. /// The to perform the resampling. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Skew( this IImageProcessingContext source, float degreesX, diff --git a/src/ImageSharp/Processing/TransformExtensions.cs b/src/ImageSharp/Processing/TransformExtensions.cs index 35f374d013..7fffb71d20 100644 --- a/src/ImageSharp/Processing/TransformExtensions.cs +++ b/src/ImageSharp/Processing/TransformExtensions.cs @@ -9,7 +9,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of composable transform operations to the type. + /// Defines extensions that allow the application of composable transform operations on an + /// using Mutate/Clone. /// public static class TransformExtensions { @@ -30,7 +31,7 @@ namespace SixLabors.ImageSharp.Processing /// The . /// The affine transform builder. /// The to perform the resampling. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Transform( this IImageProcessingContext ctx, AffineTransformBuilder builder, @@ -44,7 +45,7 @@ namespace SixLabors.ImageSharp.Processing /// The source rectangle /// The affine transform builder. /// The to perform the resampling. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Transform( this IImageProcessingContext ctx, Rectangle sourceRectangle, @@ -64,7 +65,7 @@ namespace SixLabors.ImageSharp.Processing /// The transformation matrix. /// The size of the result image. /// The to perform the resampling. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Transform( this IImageProcessingContext ctx, Rectangle sourceRectangle, @@ -82,7 +83,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image to transform. /// The affine transform builder. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Transform( this IImageProcessingContext source, ProjectiveTransformBuilder builder) => @@ -94,7 +95,7 @@ namespace SixLabors.ImageSharp.Processing /// The . /// The projective transform builder. /// The to perform the resampling. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Transform( this IImageProcessingContext ctx, ProjectiveTransformBuilder builder, @@ -108,7 +109,7 @@ namespace SixLabors.ImageSharp.Processing /// The source rectangle /// The projective transform builder. /// The to perform the resampling. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Transform( this IImageProcessingContext ctx, Rectangle sourceRectangle, @@ -128,7 +129,7 @@ namespace SixLabors.ImageSharp.Processing /// The transformation matrix. /// The size of the result image. /// The to perform the resampling. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Transform( this IImageProcessingContext ctx, Rectangle sourceRectangle, diff --git a/src/ImageSharp/Processing/VignetteExtensions.cs b/src/ImageSharp/Processing/VignetteExtensions.cs index 18dd8064c6..63cdee3f83 100644 --- a/src/ImageSharp/Processing/VignetteExtensions.cs +++ b/src/ImageSharp/Processing/VignetteExtensions.cs @@ -9,7 +9,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of a radial glow to the type. + /// Defines extensions that allow the application of a radial glow to an + /// using Mutate/Clone. /// public static class VignetteExtensions { @@ -18,7 +19,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The pixel format. /// The image this method extends. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Vignette(this IImageProcessingContext source) where TPixel : struct, IPixel => Vignette(source, GraphicsOptions.Default); @@ -29,7 +30,7 @@ namespace SixLabors.ImageSharp.Processing /// The pixel format. /// The image this method extends. /// The color to set as the vignette. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Vignette(this IImageProcessingContext source, TPixel color) where TPixel : struct, IPixel => Vignette(source, GraphicsOptions.Default, color); @@ -41,7 +42,7 @@ namespace SixLabors.ImageSharp.Processing /// The image this method extends. /// The the x-radius. /// The the y-radius. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Vignette(this IImageProcessingContext source, float radiusX, float radiusY) where TPixel : struct, IPixel => Vignette(source, GraphicsOptions.Default, radiusX, radiusY); @@ -54,7 +55,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Vignette(this IImageProcessingContext source, Rectangle rectangle) where TPixel : struct, IPixel => Vignette(source, GraphicsOptions.Default, rectangle); @@ -70,7 +71,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Vignette(this IImageProcessingContext source, TPixel color, float radiusX, float radiusY, Rectangle rectangle) where TPixel : struct, IPixel => source.Vignette(GraphicsOptions.Default, color, radiusX, radiusY, rectangle); @@ -81,7 +82,7 @@ namespace SixLabors.ImageSharp.Processing /// The pixel format. /// The image this method extends. /// The options effecting pixel blending. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Vignette(this IImageProcessingContext source, GraphicsOptions options) where TPixel : struct, IPixel => source.VignetteInternal(options, NamedColors.Black, ValueSize.PercentageOfWidth(.5f), ValueSize.PercentageOfHeight(.5f)); @@ -93,7 +94,7 @@ namespace SixLabors.ImageSharp.Processing /// The image this method extends. /// The options effecting pixel blending. /// The color to set as the vignette. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Vignette(this IImageProcessingContext source, GraphicsOptions options, TPixel color) where TPixel : struct, IPixel => source.VignetteInternal(options, color, ValueSize.PercentageOfWidth(.5f), ValueSize.PercentageOfHeight(.5f)); @@ -106,7 +107,7 @@ namespace SixLabors.ImageSharp.Processing /// The options effecting pixel blending. /// The the x-radius. /// The the y-radius. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Vignette(this IImageProcessingContext source, GraphicsOptions options, float radiusX, float radiusY) where TPixel : struct, IPixel => source.VignetteInternal(options, NamedColors.Black, radiusX, radiusY); @@ -120,7 +121,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Vignette(this IImageProcessingContext source, GraphicsOptions options, Rectangle rectangle) where TPixel : struct, IPixel => source.VignetteInternal(options, NamedColors.Black, ValueSize.PercentageOfWidth(.5f), ValueSize.PercentageOfHeight(.5f), rectangle); @@ -137,7 +138,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Vignette(this IImageProcessingContext source, GraphicsOptions options, TPixel color, float radiusX, float radiusY, Rectangle rectangle) where TPixel : struct, IPixel => source.VignetteInternal(options, color, radiusX, radiusY, rectangle); From fefb0e66a467ff5ade373802afc3a5b7ddbbbc21 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 5 May 2019 22:31:06 +0200 Subject: [PATCH 138/223] publish non-generic QuantizeProcessor --- ImageSharp.sln.DotSettings | 1 + .../Quantization/QuantizeProcessor.cs | 41 +++---------- .../Quantization/QuantizeProcessor{TPixel}.cs | 57 +++++++++++++++++++ .../Processing/QuantizeExtensions.cs | 13 ++--- 4 files changed, 70 insertions(+), 42 deletions(-) create mode 100644 src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs diff --git a/ImageSharp.sln.DotSettings b/ImageSharp.sln.DotSettings index 8e7b5dd488..5268172429 100644 --- a/ImageSharp.sln.DotSettings +++ b/ImageSharp.sln.DotSettings @@ -388,4 +388,5 @@ True True True + True \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor.cs b/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor.cs index e99f504b42..0aee0b4831 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor.cs @@ -1,59 +1,34 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Quantization { /// - /// Enables the quantization of images to reduce the number of colors used in the image palette. + /// Defines quantization processing for images to reduce the number of colors used in the image palette. /// - /// The pixel format. - internal class QuantizeProcessor : ImageProcessor - where TPixel : struct, IPixel + public class QuantizeProcessor : IImageProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// The quantizer used to reduce the color palette + /// The quantizer used to reduce the color palette. public QuantizeProcessor(IQuantizer quantizer) { - Guard.NotNull(quantizer, nameof(quantizer)); this.Quantizer = quantizer; } /// - /// Gets the quantizer + /// Gets the quantizer. /// public IQuantizer Quantizer { get; } /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + public IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel { - using (IFrameQuantizer executor = this.Quantizer.CreateFrameQuantizer(configuration)) - using (QuantizedFrame quantized = executor.QuantizeFrame(source)) - { - int paletteCount = quantized.Palette.Length - 1; - - // Not parallel to remove "quantized" closure allocation. - // We can operate directly on the source here as we've already read it to get the - // quantized result - for (int y = 0; y < source.Height; y++) - { - Span row = source.GetPixelRowSpan(y); - ReadOnlySpan quantizedPixelSpan = quantized.GetPixelSpan(); - int yy = y * source.Width; - - for (int x = 0; x < source.Width; x++) - { - int i = x + yy; - row[x] = quantized.Palette[Math.Min(paletteCount, quantizedPixelSpan[i])]; - } - } - } + return new QuantizeProcessor(this.Quantizer); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs new file mode 100644 index 0000000000..b52343a2ae --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs @@ -0,0 +1,57 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Quantization +{ + /// + /// Enables the quantization of images to reduce the number of colors used in the image palette. + /// + /// The pixel format. + internal class QuantizeProcessor : ImageProcessor + where TPixel : struct, IPixel + { + private readonly IQuantizer quantizer; + + /// + /// Initializes a new instance of the class. + /// + /// The quantizer used to reduce the color palette. + public QuantizeProcessor(IQuantizer quantizer) + { + Guard.NotNull(quantizer, nameof(quantizer)); + this.quantizer = quantizer; + } + + /// + protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + using (IFrameQuantizer executor = this.quantizer.CreateFrameQuantizer(configuration)) + using (QuantizedFrame quantized = executor.QuantizeFrame(source)) + { + int paletteCount = quantized.Palette.Length - 1; + + // Not parallel to remove "quantized" closure allocation. + // We can operate directly on the source here as we've already read it to get the + // quantized result + for (int y = 0; y < source.Height; y++) + { + Span row = source.GetPixelRowSpan(y); + ReadOnlySpan quantizedPixelSpan = quantized.GetPixelSpan(); + int yy = y * source.Width; + + for (int x = 0; x < source.Width; x++) + { + int i = x + yy; + row[x] = quantized.Palette[Math.Min(paletteCount, quantizedPixelSpan[i])]; + } + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/QuantizeExtensions.cs b/src/ImageSharp/Processing/QuantizeExtensions.cs index ad351afa99..3410ee6bec 100644 --- a/src/ImageSharp/Processing/QuantizeExtensions.cs +++ b/src/ImageSharp/Processing/QuantizeExtensions.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Quantization; namespace SixLabors.ImageSharp.Processing @@ -15,22 +14,18 @@ namespace SixLabors.ImageSharp.Processing /// /// Applies quantization to the image using the . /// - /// The pixel format. /// The image this method extends. /// The to allow chaining of operations. - public static IImageProcessingContext Quantize(this IImageProcessingContext source) - where TPixel : struct, IPixel - => Quantize(source, KnownQuantizers.Octree); + public static IImageProcessingContext Quantize(this IImageProcessingContext source) => + Quantize(source, KnownQuantizers.Octree); /// /// Applies quantization to the image. /// - /// The pixel format. /// The image this method extends. /// The quantizer to apply to perform the operation. /// The to allow chaining of operations. - public static IImageProcessingContext Quantize(this IImageProcessingContext source, IQuantizer quantizer) - where TPixel : struct, IPixel - => source.ApplyProcessor(new QuantizeProcessor(quantizer)); + public static IImageProcessingContext Quantize(this IImageProcessingContext source, IQuantizer quantizer) => + source.ApplyProcessor(new QuantizeProcessor(quantizer)); } } \ No newline at end of file From 43c78abf321ce16e486b5487c95e9579f92ed441 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 5 May 2019 23:14:41 +0200 Subject: [PATCH 139/223] temporarily disable multitargeting --- src/ImageSharp.Drawing/ImageSharp.Drawing.csproj | 3 ++- src/ImageSharp/ImageSharp.csproj | 3 ++- tests/ImageSharp.Tests/ImageSharp.Tests.csproj | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj index 915915f6cc..fdeb27a770 100644 --- a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj +++ b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj @@ -11,7 +11,8 @@ $(packageversion) 0.0.1 - netcoreapp2.1;netstandard1.3;netstandard2.0 + + netstandard2.0 7.3 true diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index d734648f41..4abe2f57a2 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -11,7 +11,8 @@ $(packageversion) 0.0.1 - netcoreapp2.1;netstandard1.3;netstandard2.0;net472 + + netstandard2.0 true true diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index 0b727f30ce..efdf89964c 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -1,6 +1,7 @@  - netcoreapp2.1;net462;net472 + + netcoreapp2.1 True latest full From 2e402ba35da09efd6367964ef6118e96246101af Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 5 May 2019 23:30:32 +0200 Subject: [PATCH 140/223] add skeleton for Color type --- src/ImageSharp/Color.cs | 42 +++++++++++++++++++ .../PixelImplementations/Rgba64.cs | 15 +++++++ .../Codecs/GetSetPixel.cs | 4 +- .../Drawing/DrawBeziers.cs | 2 +- .../Drawing/DrawLines.cs | 2 +- .../Drawing/DrawPolygon.cs | 2 +- .../Drawing/DrawTextOutline.cs | 2 +- .../Drawing/FillWithPattern.cs | 2 +- tests/ImageSharp.Tests/ColorTests.cs | 10 +++++ 9 files changed, 74 insertions(+), 7 deletions(-) create mode 100644 src/ImageSharp/Color.cs create mode 100644 tests/ImageSharp.Tests/ColorTests.cs diff --git a/src/ImageSharp/Color.cs b/src/ImageSharp/Color.cs new file mode 100644 index 0000000000..5b3de2eee3 --- /dev/null +++ b/src/ImageSharp/Color.cs @@ -0,0 +1,42 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp +{ + public struct Color + { + private Rgba64 data; + + public Color(Rgba64 pixel) + { + this.data = pixel; + } + + public Color(Rgba32 pixel) + { + this.data = default; + this.data.FromRgba32(pixel); + } + + public Color(Vector4 vector) + { + this.data = default; + this.data.FromVector4(vector); + } + + public static Color FromRgba(byte r, byte g, byte b, byte a) => new Color(new Rgba32(r, g, b, a)); + public static Color FromRgba(ushort r, ushort g, ushort b, ushort a) => new Color(new Rgba64(r, g, b, a)); + + public TPixel ToPixel() + where TPixel : struct, IPixel + { + TPixel pixel = default; + pixel.FromRgba64(this.data); + return pixel; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs index 87d1b235bf..6d838a54dd 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs @@ -54,6 +54,21 @@ namespace SixLabors.ImageSharp.PixelFormats this.A = a; } + /// + /// Initializes a new instance of the struct. + /// + /// The red component. + /// The green component. + /// The blue component. + /// The alpha component. + public Rgba64(byte r, byte g, byte b, byte a) + { + this.R = r; + this.G = g; + this.B = b; + this.A = a; + } + /// /// Gets or sets the RGB components of this struct as /// diff --git a/tests/ImageSharp.Benchmarks/Codecs/GetSetPixel.cs b/tests/ImageSharp.Benchmarks/Codecs/GetSetPixel.cs index a51ce8ebc6..f0d7a54d08 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/GetSetPixel.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/GetSetPixel.cs @@ -10,11 +10,11 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs public class GetSetPixel : BenchmarkBase { [Benchmark(Baseline = true, Description = "System.Drawing GetSet pixel")] - public Color ResizeSystemDrawing() + public System.Drawing.Color ResizeSystemDrawing() { using (var source = new Bitmap(400, 400)) { - source.SetPixel(200, 200, Color.White); + source.SetPixel(200, 200, System.Drawing.Color.White); return source.GetPixel(200, 200); } } diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs index bc9c1c96d2..0a343864d8 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs @@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Benchmarks graphics.InterpolationMode = InterpolationMode.Default; graphics.SmoothingMode = SmoothingMode.AntiAlias; - using (var pen = new Pen(Color.HotPink, 10)) + using (var pen = new Pen(System.Drawing.Color.HotPink, 10)) { graphics.DrawBeziers(pen, new[] { new PointF(10, 500), diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs index 4265525e5b..00942cc364 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Benchmarks graphics.InterpolationMode = InterpolationMode.Default; graphics.SmoothingMode = SmoothingMode.AntiAlias; - using (var pen = new Pen(Color.HotPink, 10)) + using (var pen = new Pen(System.Drawing.Color.HotPink, 10)) { graphics.DrawLines(pen, new[] { new PointF(10, 10), diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs index 4172b3c38f..dcaf4bae60 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs @@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Benchmarks { graphics.InterpolationMode = InterpolationMode.Default; graphics.SmoothingMode = SmoothingMode.AntiAlias; - using (var pen = new Pen(Color.HotPink, 10)) + using (var pen = new Pen(System.Drawing.Color.HotPink, 10)) { graphics.DrawPolygon(pen, new[] { new PointF(10, 10), diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawTextOutline.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawTextOutline.cs index b99c47960b..ee17adbb55 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/DrawTextOutline.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/DrawTextOutline.cs @@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Benchmarks { graphics.InterpolationMode = InterpolationMode.Default; graphics.SmoothingMode = SmoothingMode.AntiAlias; - using (var pen = new Pen(Color.HotPink, 10)) + using (var pen = new Pen(System.Drawing.Color.HotPink, 10)) using (var font = new Font("Arial", 12, GraphicsUnit.Point)) using (var gp = new GraphicsPath()) { diff --git a/tests/ImageSharp.Benchmarks/Drawing/FillWithPattern.cs b/tests/ImageSharp.Benchmarks/Drawing/FillWithPattern.cs index 3c9cfd7e34..411f8210a9 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/FillWithPattern.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/FillWithPattern.cs @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Benchmarks { graphics.SmoothingMode = SmoothingMode.AntiAlias; - using (var brush = new HatchBrush(HatchStyle.BackwardDiagonal, Color.HotPink)) + using (var brush = new HatchBrush(HatchStyle.BackwardDiagonal, System.Drawing.Color.HotPink)) { graphics.FillRectangle(brush, new Rectangle(0, 0, 800, 800)); // can't find a way to flood fill with a brush } diff --git a/tests/ImageSharp.Tests/ColorTests.cs b/tests/ImageSharp.Tests/ColorTests.cs new file mode 100644 index 0000000000..4142066bc8 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorTests.cs @@ -0,0 +1,10 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Tests +{ + public class ColorTests + { + + } +} \ No newline at end of file From 99916fb452b577b50ec65983557d846b4c13d52d Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 5 May 2019 23:52:39 +0200 Subject: [PATCH 141/223] add more Rgba64 constructor overloads --- src/ImageSharp/Color.cs | 6 +- .../PixelImplementations/Rgba64.cs | 146 +++++++++++- .../PixelFormats/Rgba64Tests.cs | 215 ++++++++++++++++-- 3 files changed, 336 insertions(+), 31 deletions(-) diff --git a/src/ImageSharp/Color.cs b/src/ImageSharp/Color.cs index 5b3de2eee3..4f6cab97bb 100644 --- a/src/ImageSharp/Color.cs +++ b/src/ImageSharp/Color.cs @@ -18,8 +18,7 @@ namespace SixLabors.ImageSharp public Color(Rgba32 pixel) { - this.data = default; - this.data.FromRgba32(pixel); + this.data = new Rgba64(pixel); } public Color(Vector4 vector) @@ -29,8 +28,7 @@ namespace SixLabors.ImageSharp } public static Color FromRgba(byte r, byte g, byte b, byte a) => new Color(new Rgba32(r, g, b, a)); - public static Color FromRgba(ushort r, ushort g, ushort b, ushort a) => new Color(new Rgba64(r, g, b, a)); - + public TPixel ToPixel() where TPixel : struct, IPixel { diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs index 6d838a54dd..cf9d4a5527 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs @@ -57,20 +57,78 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// Initializes a new instance of the struct. /// - /// The red component. - /// The green component. - /// The blue component. - /// The alpha component. - public Rgba64(byte r, byte g, byte b, byte a) + /// A structure of 4 bytes in RGBA byte order. + public Rgba64(Rgba32 source) { - this.R = r; - this.G = g; - this.B = b; - this.A = a; + this.R = ImageMaths.UpscaleFrom8BitTo16Bit(source.R); + this.G = ImageMaths.UpscaleFrom8BitTo16Bit(source.G); + this.B = ImageMaths.UpscaleFrom8BitTo16Bit(source.B); + this.A = ImageMaths.UpscaleFrom8BitTo16Bit(source.A); } /// - /// Gets or sets the RGB components of this struct as + /// Initializes a new instance of the struct. + /// + /// A structure of 4 bytes in BGRA byte order. + public Rgba64(Bgra32 source) + { + this.R = ImageMaths.UpscaleFrom8BitTo16Bit(source.R); + this.G = ImageMaths.UpscaleFrom8BitTo16Bit(source.G); + this.B = ImageMaths.UpscaleFrom8BitTo16Bit(source.B); + this.A = ImageMaths.UpscaleFrom8BitTo16Bit(source.A); + } + + /// + /// Initializes a new instance of the struct. + /// + /// A structure of 4 bytes in ARGB byte order. + public Rgba64(Argb32 source) + { + this.R = ImageMaths.UpscaleFrom8BitTo16Bit(source.R); + this.G = ImageMaths.UpscaleFrom8BitTo16Bit(source.G); + this.B = ImageMaths.UpscaleFrom8BitTo16Bit(source.B); + this.A = ImageMaths.UpscaleFrom8BitTo16Bit(source.A); + } + + /// + /// Initializes a new instance of the struct. + /// + /// A structure of 3 bytes in RGB byte order. + public Rgba64(Rgb24 source) + { + this.R = ImageMaths.UpscaleFrom8BitTo16Bit(source.R); + this.G = ImageMaths.UpscaleFrom8BitTo16Bit(source.G); + this.B = ImageMaths.UpscaleFrom8BitTo16Bit(source.B); + this.A = ushort.MaxValue; + } + + /// + /// Initializes a new instance of the struct. + /// + /// A structure of 3 bytes in BGR byte order. + public Rgba64(Bgr24 source) + { + this.R = ImageMaths.UpscaleFrom8BitTo16Bit(source.R); + this.G = ImageMaths.UpscaleFrom8BitTo16Bit(source.G); + this.B = ImageMaths.UpscaleFrom8BitTo16Bit(source.B); + this.A = ushort.MaxValue; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The . + public Rgba64(Vector4 vector) + { + vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One) * Max; + this.R = (ushort)MathF.Round(vector.X); + this.G = (ushort)MathF.Round(vector.Y); + this.B = (ushort)MathF.Round(vector.Z); + this.A = (ushort)MathF.Round(vector.W); + } + + /// + /// Gets or sets the RGB components of this struct as . /// public Rgb48 Rgb { @@ -236,6 +294,74 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public void FromRgba64(Rgba64 source) => this = source; + /// + /// Convert to . + /// + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public Rgba32 ToRgba32() + { + byte r = ImageMaths.DownScaleFrom16BitTo8Bit(this.R); + byte g = ImageMaths.DownScaleFrom16BitTo8Bit(this.G); + byte b = ImageMaths.DownScaleFrom16BitTo8Bit(this.B); + byte a = ImageMaths.DownScaleFrom16BitTo8Bit(this.A); + return new Rgba32(r, g, b, a); + } + + /// + /// Convert to . + /// + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public Bgra32 ToBgra32() + { + byte r = ImageMaths.DownScaleFrom16BitTo8Bit(this.R); + byte g = ImageMaths.DownScaleFrom16BitTo8Bit(this.G); + byte b = ImageMaths.DownScaleFrom16BitTo8Bit(this.B); + byte a = ImageMaths.DownScaleFrom16BitTo8Bit(this.A); + return new Bgra32(r, g, b, a); + } + + /// + /// Convert to . + /// + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public Argb32 ToArgb32() + { + byte r = ImageMaths.DownScaleFrom16BitTo8Bit(this.R); + byte g = ImageMaths.DownScaleFrom16BitTo8Bit(this.G); + byte b = ImageMaths.DownScaleFrom16BitTo8Bit(this.B); + byte a = ImageMaths.DownScaleFrom16BitTo8Bit(this.A); + return new Argb32(r, g, b, a); + } + + /// + /// Convert to . + /// + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public Rgb24 ToRgb24() + { + byte r = ImageMaths.DownScaleFrom16BitTo8Bit(this.R); + byte g = ImageMaths.DownScaleFrom16BitTo8Bit(this.G); + byte b = ImageMaths.DownScaleFrom16BitTo8Bit(this.B); + return new Rgb24(r, g, b); + } + + /// + /// Convert to . + /// + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public Bgr24 ToBgr24() + { + byte r = ImageMaths.DownScaleFrom16BitTo8Bit(this.R); + byte g = ImageMaths.DownScaleFrom16BitTo8Bit(this.G); + byte b = ImageMaths.DownScaleFrom16BitTo8Bit(this.B); + return new Bgr24(r, g, b); + } + /// public override bool Equals(object obj) => obj is Rgba64 rgba64 && this.Equals(rgba64); diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs index 51f80e0e18..55a2dda7a9 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs @@ -33,37 +33,50 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(Vector4.One, new Rgba64(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, ushort.MaxValue).ToVector4()); } - [Fact] - public void Rgba64_ToScaledVector4() + [Theory] + [InlineData(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)] + [InlineData(0, 0, 0, 0)] + [InlineData(ushort.MaxValue/2, 100, 2222, 33333)] + public void Rgba64_ToScaledVector4(ushort r, ushort g, ushort b, ushort a) { // arrange - var short2 = new Rgba64(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, ushort.MaxValue); + var short2 = new Rgba64(r, g, b, a); + + float max = ushort.MaxValue; + float rr = r / max; + float gg = g / max; + float bb = b / max; + float aa = a / max; // act Vector4 actual = short2.ToScaledVector4(); // assert - Assert.Equal(1, actual.X); - Assert.Equal(1, actual.Y); - Assert.Equal(1, actual.Z); - Assert.Equal(1, actual.W); + Assert.Equal(rr, actual.X); + Assert.Equal(gg, actual.Y); + Assert.Equal(bb, actual.Z); + Assert.Equal(aa, actual.W); } - [Fact] - public void Rgba64_FromScaledVector4() + [Theory] + [InlineData(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)] + [InlineData(0, 0, 0, 0)] + [InlineData(ushort.MaxValue/2, 100, 2222, 33333)] + public void Rgba64_FromScaledVector4(ushort r, ushort g, ushort b, ushort a) { // arrange - var pixel = default(Rgba64); - var short4 = new Rgba64(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, ushort.MaxValue); - const ulong expected = 0xFFFFFFFFFFFFFFFF; - + + var source = new Rgba64(r, g, b, a); + // act - Vector4 scaled = short4.ToScaledVector4(); - pixel.FromScaledVector4(scaled); - ulong actual = pixel.PackedValue; + Vector4 scaled = source.ToScaledVector4(); + + Rgba64 actual = default; + actual.FromScaledVector4(scaled); + // assert - Assert.Equal(expected, actual); + Assert.Equal(source, actual); } [Fact] @@ -91,6 +104,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats // assert Assert.Equal(expected, actual); } + [Fact] public void Rgba64_FromBgra5551() @@ -108,5 +122,172 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(expected, rgba.B); Assert.Equal(expected, rgba.A); } + + [Fact] + public void Equality_WhenTrue() + { + Rgba64 c1 = new Rgba64(100, 2000, 3000, 40000); + Rgba64 c2 = new Rgba64(100, 2000, 3000, 40000); + + Assert.True(c1.Equals(c2)); + Assert.True(c1.GetHashCode() == c2.GetHashCode()); + } + + [Fact] + public void Equality_WhenFalse() + { + Rgba64 c1 = new Rgba64(100, 2000, 3000, 40000); + Rgba64 c2 = new Rgba64(101, 2000, 3000, 40000); + Rgba64 c3 = new Rgba64(100, 2000, 3000, 40001); + + Assert.False(c1.Equals(c2)); + Assert.False(c2.Equals(c3)); + Assert.False(c3.Equals(c1)); + } + + [Fact] + public void Rgba64_FromRgba32() + { + var source = new Rgba32(20, 38, 76, 115); + var expected = new Rgba64(5140, 9766, 19532, 29555); + + Rgba64 actual = default; + actual.FromRgba32(source); + + Assert.Equal(expected, actual); + } + + [Fact] + public void ConstructFrom_Rgba32() + { + var expected = new Rgba64(5140, 9766, 19532, 29555); + var source = new Rgba32(20, 38, 76, 115); + Rgba64 actual = new Rgba64(source); + + Assert.Equal(expected, actual); + } + + [Fact] + public void ConstructFrom_Bgra32() + { + var expected = new Rgba64(5140, 9766, 19532, 29555); + var source = new Bgra32(20, 38, 76, 115); + Rgba64 actual = new Rgba64(source); + + Assert.Equal(expected, actual); + } + + [Fact] + public void ConstructFrom_Argb32() + { + var expected = new Rgba64(5140, 9766, 19532, 29555); + var source = new Argb32(20, 38, 76, 115); + Rgba64 actual = new Rgba64(source); + + Assert.Equal(expected, actual); + } + + [Fact] + public void ConstructFrom_Rgb24() + { + var expected = new Rgba64(5140, 9766, 19532, ushort.MaxValue); + var source = new Rgb24(20, 38, 76); + Rgba64 actual = new Rgba64(source); + + Assert.Equal(expected, actual); + } + + [Fact] + public void ConstructFrom_Bgr24() + { + var expected = new Rgba64(5140, 9766, 19532, ushort.MaxValue); + var source = new Bgr24(20, 38, 76); + Rgba64 actual = new Rgba64(source); + + Assert.Equal(expected, actual); + } + + [Fact] + public void ConstructFrom_Vector4() + { + Vector4 source = new Vector4(0f, 0.2f, 0.5f, 1f); + Rgba64 expected = default; + expected.FromScaledVector4(source); + + Rgba64 actual = new Rgba64(source); + + Assert.Equal(expected, actual); + } + + + [Fact] + public void ToRgba32_Retval() + { + // arrange + var source = new Rgba64(5140, 9766, 19532, 29555); + var expected = new Rgba32(20, 38, 76, 115); + + // act + var actual = source.ToRgba32(); + + // assert + Assert.Equal(expected, actual); + } + + [Fact] + public void ToBgra32_Retval() + { + // arrange + var source = new Rgba64(5140, 9766, 19532, 29555); + var expected = new Bgra32(20, 38, 76, 115); + + // act + var actual = source.ToBgra32(); + + // assert + Assert.Equal(expected, actual); + } + + [Fact] + public void ToArgb32_Retval() + { + // arrange + var source = new Rgba64(5140, 9766, 19532, 29555); + var expected = new Argb32(20, 38, 76, 115); + + // act + var actual = source.ToArgb32(); + + // assert + Assert.Equal(expected, actual); + } + + [Fact] + public void ToRgb24_Retval() + { + // arrange + var source = new Rgba64(5140, 9766, 19532, 29555); + var expected = new Rgb24(20, 38, 76); + + // act + var actual = source.ToRgb24(); + + // assert + Assert.Equal(expected, actual); + } + + [Fact] + public void ToBgr24_Retval() + { + // arrange + var source = new Rgba64(5140, 9766, 19532, 29555); + var expected = new Bgr24(20, 38, 76); + + // act + var actual = source.ToBgr24(); + + // assert + Assert.Equal(expected, actual); + } } } From 2e53d72c4911e092c4e262b0cf9131ea4b5cf81e Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 6 May 2019 00:41:19 +0200 Subject: [PATCH 142/223] basic Color methods --- src/ImageSharp/Color.cs | 51 +++++- tests/ImageSharp.Tests/ColorTests.cs | 245 +++++++++++++++++++++++++++ 2 files changed, 292 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/Color.cs b/src/ImageSharp/Color.cs index 4f6cab97bb..70c721060a 100644 --- a/src/ImageSharp/Color.cs +++ b/src/ImageSharp/Color.cs @@ -7,9 +7,9 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp { - public struct Color + public readonly struct Color { - private Rgba64 data; + private readonly Rgba64 data; public Color(Rgba64 pixel) { @@ -21,12 +21,55 @@ namespace SixLabors.ImageSharp this.data = new Rgba64(pixel); } + public Color(Argb32 pixel) + { + this.data = new Rgba64(pixel); + } + + public Color(Bgra32 pixel) + { + this.data = new Rgba64(pixel); + } + + public Color(Rgb24 pixel) + { + this.data = new Rgba64(pixel); + } + + public Color(Bgr24 pixel) + { + this.data = new Rgba64(pixel); + } + public Color(Vector4 vector) { - this.data = default; - this.data.FromVector4(vector); + this.data = new Rgba64(vector); } + public static implicit operator Color(Rgba64 source) => new Color(source); + + public static implicit operator Color(Rgba32 source) => new Color(source); + + public static implicit operator Color(Bgra32 source) => new Color(source); + + public static implicit operator Color(Argb32 source) => new Color(source); + + public static implicit operator Color(Rgb24 source) => new Color(source); + + public static implicit operator Color(Bgr24 source) => new Color(source); + + public static implicit operator Rgba64(Color color) => color.data; + + public static implicit operator Rgba32(Color color) => color.data.ToRgba32(); + + public static implicit operator Bgra32(Color color) => color.data.ToBgra32(); + + public static implicit operator Argb32(Color color) => color.data.ToArgb32(); + + public static implicit operator Rgb24(Color color) => color.data.ToRgb24(); + + public static implicit operator Bgr24(Color color) => color.data.ToBgr24(); + public static Color FromRgba(byte r, byte g, byte b, byte a) => new Color(new Rgba32(r, g, b, a)); public TPixel ToPixel() diff --git a/tests/ImageSharp.Tests/ColorTests.cs b/tests/ImageSharp.Tests/ColorTests.cs index 4142066bc8..a1eaf9fae3 100644 --- a/tests/ImageSharp.Tests/ColorTests.cs +++ b/tests/ImageSharp.Tests/ColorTests.cs @@ -1,10 +1,255 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.PixelFormats; + +using Xunit; + namespace SixLabors.ImageSharp.Tests { public class ColorTests { + public class ConstructFrom + { + [Fact] + public void Rgba64() + { + Rgba64 source = new Rgba64(100, 2222, 3333, 4444); + + // Act: + Color color = new Color(source); + + // Assert: + Rgba64 data = color.ToPixel(); + Assert.Equal(source, data); + } + + [Fact] + public void Rgba32() + { + Rgba32 source = new Rgba32(1, 22, 33, 231); + + // Act: + Color color = new Color(source); + + // Assert: + Rgba32 data = color.ToPixel(); + Assert.Equal(source, data); + } + + [Fact] + public void Argb32() + { + Argb32 source = new Argb32(1, 22, 33, 231); + + // Act: + Color color = new Color(source); + + // Assert: + Argb32 data = color.ToPixel(); + Assert.Equal(source, data); + } + + [Fact] + public void Bgra32() + { + Bgra32 source = new Bgra32(1, 22, 33, 231); + + // Act: + Color color = new Color(source); + + // Assert: + Bgra32 data = color.ToPixel(); + Assert.Equal(source, data); + } + + [Fact] + public void Rgb24() + { + Rgb24 source = new Rgb24(1, 22, 231); + + // Act: + Color color = new Color(source); + + // Assert: + Rgb24 data = color.ToPixel(); + Assert.Equal(source, data); + } + + [Fact] + public void Bgr24() + { + Bgr24 source = new Bgr24(1, 22, 231); + + // Act: + Color color = new Color(source); + + // Assert: + Bgr24 data = color.ToPixel(); + Assert.Equal(source, data); + } + } + public class Cast + { + [Fact] + public void Rgba64() + { + Rgba64 source = new Rgba64(100, 2222, 3333, 4444); + + // Act: + Color color = source; + + // Assert: + Rgba64 data = color.ToPixel(); + Assert.Equal(source, data); + } + + [Fact] + public void Rgba32() + { + Rgba32 source = new Rgba32(1, 22, 33, 231); + + // Act: + Color color = source; + + // Assert: + Rgba32 data = color.ToPixel(); + Assert.Equal(source, data); + } + + [Fact] + public void Argb32() + { + Argb32 source = new Argb32(1, 22, 33, 231); + + // Act: + Color color = source; + + // Assert: + Argb32 data = color.ToPixel(); + Assert.Equal(source, data); + } + + [Fact] + public void Bgra32() + { + Bgra32 source = new Bgra32(1, 22, 33, 231); + + // Act: + Color color = source; + + // Assert: + Bgra32 data = color.ToPixel(); + Assert.Equal(source, data); + } + + [Fact] + public void Rgb24() + { + Rgb24 source = new Rgb24(1, 22, 231); + + // Act: + Color color = source; + + // Assert: + Rgb24 data = color.ToPixel(); + Assert.Equal(source, data); + } + + [Fact] + public void Bgr24() + { + Bgr24 source = new Bgr24(1, 22, 231); + + // Act: + Color color = source; + + // Assert: + Bgr24 data = color.ToPixel(); + Assert.Equal(source, data); + } + } + + public class CastTo + { + [Fact] + public void Rgba64() + { + Rgba64 source = new Rgba64(100, 2222, 3333, 4444); + + // Act: + Color color = new Color(source); + + // Assert: + Rgba64 data = color; + Assert.Equal(source, data); + } + + [Fact] + public void Rgba32() + { + Rgba32 source = new Rgba32(1, 22, 33, 231); + + // Act: + Color color = new Color(source); + + // Assert: + Rgba32 data = color; + Assert.Equal(source, data); + } + + [Fact] + public void Argb32() + { + Argb32 source = new Argb32(1, 22, 33, 231); + + // Act: + Color color = new Color(source); + + // Assert: + Argb32 data = color; + Assert.Equal(source, data); + } + + [Fact] + public void Bgra32() + { + Bgra32 source = new Bgra32(1, 22, 33, 231); + + // Act: + Color color = new Color(source); + + // Assert: + Bgra32 data = color; + Assert.Equal(source, data); + } + + [Fact] + public void Rgb24() + { + Rgb24 source = new Rgb24(1, 22, 231); + + // Act: + Color color = new Color(source); + + // Assert: + Rgb24 data = color; + Assert.Equal(source, data); + } + + [Fact] + public void Bgr24() + { + Bgr24 source = new Bgr24(1, 22, 231); + + // Act: + Color color = new Color(source); + + // Assert: + Bgr24 data = color; + Assert.Equal(source, data); + } + } } } \ No newline at end of file From e72b7172dd92cb5127c234943c0b85e9882ce4d1 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 6 May 2019 01:12:41 +0200 Subject: [PATCH 143/223] Pixel-agnostic Binarization processors --- src/ImageSharp/Color.cs | 2 +- .../Processing/BinaryDiffuseExtensions.cs | 47 +- .../Processing/BinaryDitherExtensions.cs | 40 +- .../Processing/BinaryThresholdExtensions.cs | 49 +- .../BinaryErrorDiffusionProcessor.cs | 68 +- .../BinaryErrorDiffusionProcessor{TPixel}.cs | 77 ++ .../BinaryOrderedDitherProcessor.cs | 67 +- .../BinaryOrderedDitherProcessor{TPixel}.cs | 75 ++ .../Binarization/BinaryThresholdProcessor.cs | 63 +- .../BinaryThresholdProcessor{TPixel}.cs | 70 ++ .../EdgeDetector2DProcessor{TPixel}.cs | 2 +- .../EdgeDetectorCompassProcessor{TPixel}.cs | 2 +- .../EdgeDetectorProcessor{TPixel}.cs | 2 +- .../Processors/Convolution/SobelProcessor.cs | 8 - .../Filters/GrayscaleBt709Processor.cs | 8 - .../Processors/ImageProcessorExtensions.cs | 15 + .../EntropyCropProcessor{TPixel}.cs | 4 +- .../Source/ImageSharp/Color.NamedColors.cs | 717 ++++++++++++++++++ .../Binarization/BinaryDitherTest.cs | 54 +- .../Binarization/BinaryThresholdTest.cs | 28 +- 20 files changed, 1114 insertions(+), 284 deletions(-) create mode 100644 src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor{TPixel}.cs create mode 100644 src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor{TPixel}.cs create mode 100644 src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs create mode 100644 src/ImageSharp/Source/ImageSharp/Color.NamedColors.cs diff --git a/src/ImageSharp/Color.cs b/src/ImageSharp/Color.cs index 70c721060a..6dae76c34d 100644 --- a/src/ImageSharp/Color.cs +++ b/src/ImageSharp/Color.cs @@ -7,7 +7,7 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp { - public readonly struct Color + public readonly partial struct Color { private readonly Rgba64 data; diff --git a/src/ImageSharp/Processing/BinaryDiffuseExtensions.cs b/src/ImageSharp/Processing/BinaryDiffuseExtensions.cs index 487b64e1c4..7976daf25e 100644 --- a/src/ImageSharp/Processing/BinaryDiffuseExtensions.cs +++ b/src/ImageSharp/Processing/BinaryDiffuseExtensions.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Binarization; using SixLabors.ImageSharp.Processing.Processors.Dithering; using SixLabors.Primitives; @@ -9,7 +8,7 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Defines extension methods to apply binary diffusion on an + /// Defines extension methods to apply binary diffusion on an /// using Mutate/Clone. /// public static class BinaryDiffuseExtensions @@ -17,19 +16,19 @@ namespace SixLabors.ImageSharp.Processing /// /// Dithers the image reducing it to two colors using error diffusion. /// - /// The pixel format. /// The image this method extends. /// The diffusion algorithm to apply. /// The threshold to apply binarization of the image. Must be between 0 and 1. /// The to allow chaining of operations. - public static IImageProcessingContext BinaryDiffuse(this IImageProcessingContext source, IErrorDiffuser diffuser, float threshold) - where TPixel : struct, IPixel - => source.ApplyProcessor(new BinaryErrorDiffusionProcessor(diffuser, threshold)); + public static IImageProcessingContext BinaryDiffuse( + this IImageProcessingContext source, + IErrorDiffuser diffuser, + float threshold) => + source.ApplyProcessor(new BinaryErrorDiffusionProcessor(diffuser, threshold)); /// /// Dithers the image reducing it to two colors using error diffusion. /// - /// The pixel format. /// The image this method extends. /// The diffusion algorithm to apply. /// The threshold to apply binarization of the image. Must be between 0 and 1. @@ -37,28 +36,33 @@ namespace SixLabors.ImageSharp.Processing /// The structure that specifies the portion of the image object to alter. /// /// The to allow chaining of operations. - public static IImageProcessingContext BinaryDiffuse(this IImageProcessingContext source, IErrorDiffuser diffuser, float threshold, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new BinaryErrorDiffusionProcessor(diffuser, threshold), rectangle); + public static IImageProcessingContext BinaryDiffuse( + this IImageProcessingContext source, + IErrorDiffuser diffuser, + float threshold, + Rectangle rectangle) => + source.ApplyProcessor(new BinaryErrorDiffusionProcessor(diffuser, threshold), rectangle); /// /// Dithers the image reducing it to two colors using error diffusion. /// - /// The pixel format. /// The image this method extends. /// The diffusion algorithm to apply. /// The threshold to apply binarization of the image. Must be between 0 and 1. /// The color to use for pixels that are above the threshold. /// The color to use for pixels that are below the threshold /// The to allow chaining of operations. - public static IImageProcessingContext BinaryDiffuse(this IImageProcessingContext source, IErrorDiffuser diffuser, float threshold, TPixel upperColor, TPixel lowerColor) - where TPixel : struct, IPixel - => source.ApplyProcessor(new BinaryErrorDiffusionProcessor(diffuser, threshold, upperColor, lowerColor)); + public static IImageProcessingContext BinaryDiffuse( + this IImageProcessingContext source, + IErrorDiffuser diffuser, + float threshold, + Color upperColor, + Color lowerColor) => + source.ApplyProcessor(new BinaryErrorDiffusionProcessor(diffuser, threshold, upperColor, lowerColor)); /// /// Dithers the image reducing it to two colors using error diffusion. /// - /// The pixel format. /// The image this method extends. /// The diffusion algorithm to apply. /// The threshold to apply binarization of the image. Must be between 0 and 1. @@ -68,8 +72,15 @@ namespace SixLabors.ImageSharp.Processing /// The structure that specifies the portion of the image object to alter. /// /// The to allow chaining of operations. - public static IImageProcessingContext BinaryDiffuse(this IImageProcessingContext source, IErrorDiffuser diffuser, float threshold, TPixel upperColor, TPixel lowerColor, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new BinaryErrorDiffusionProcessor(diffuser, threshold, upperColor, lowerColor), rectangle); + public static IImageProcessingContext BinaryDiffuse( + this IImageProcessingContext source, + IErrorDiffuser diffuser, + float threshold, + Color upperColor, + Color lowerColor, + Rectangle rectangle) => + source.ApplyProcessor( + new BinaryErrorDiffusionProcessor(diffuser, threshold, upperColor, lowerColor), + rectangle); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/BinaryDitherExtensions.cs b/src/ImageSharp/Processing/BinaryDitherExtensions.cs index d8843dafac..b1e3d562ed 100644 --- a/src/ImageSharp/Processing/BinaryDitherExtensions.cs +++ b/src/ImageSharp/Processing/BinaryDitherExtensions.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Binarization; using SixLabors.ImageSharp.Processing.Processors.Dithering; using SixLabors.Primitives; @@ -9,7 +8,7 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Defines extensions to apply binary dithering on an + /// Defines extensions to apply binary dithering on an /// using Mutate/Clone. /// public static class BinaryDitherExtensions @@ -17,45 +16,46 @@ namespace SixLabors.ImageSharp.Processing /// /// Dithers the image reducing it to two colors using ordered dithering. /// - /// The pixel format. /// The image this method extends. /// The ordered ditherer. /// The to allow chaining of operations. - public static IImageProcessingContext BinaryDither(this IImageProcessingContext source, IOrderedDither dither) - where TPixel : struct, IPixel - => source.ApplyProcessor(new BinaryOrderedDitherProcessor(dither)); + public static IImageProcessingContext + BinaryDither(this IImageProcessingContext source, IOrderedDither dither) => + source.ApplyProcessor(new BinaryOrderedDitherProcessor(dither)); /// /// Dithers the image reducing it to two colors using ordered dithering. /// - /// The pixel format. /// The image this method extends. /// The ordered ditherer. /// The color to use for pixels that are above the threshold. /// The color to use for pixels that are below the threshold /// The to allow chaining of operations. - public static IImageProcessingContext BinaryDither(this IImageProcessingContext source, IOrderedDither dither, TPixel upperColor, TPixel lowerColor) - where TPixel : struct, IPixel - => source.ApplyProcessor(new BinaryOrderedDitherProcessor(dither, upperColor, lowerColor)); + public static IImageProcessingContext BinaryDither( + this IImageProcessingContext source, + IOrderedDither dither, + Color upperColor, + Color lowerColor) => + source.ApplyProcessor(new BinaryOrderedDitherProcessor(dither, upperColor, lowerColor)); /// /// Dithers the image reducing it to two colors using ordered dithering. /// - /// The pixel format. /// The image this method extends. /// The ordered ditherer. /// /// The structure that specifies the portion of the image object to alter. /// /// The to allow chaining of operations. - public static IImageProcessingContext BinaryDither(this IImageProcessingContext source, IOrderedDither dither, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new BinaryOrderedDitherProcessor(dither), rectangle); + public static IImageProcessingContext BinaryDither( + this IImageProcessingContext source, + IOrderedDither dither, + Rectangle rectangle) => + source.ApplyProcessor(new BinaryOrderedDitherProcessor(dither), rectangle); /// /// Dithers the image reducing it to two colors using ordered dithering. /// - /// The pixel format. /// The image this method extends. /// The ordered ditherer. /// The color to use for pixels that are above the threshold. @@ -64,8 +64,12 @@ namespace SixLabors.ImageSharp.Processing /// The structure that specifies the portion of the image object to alter. /// /// The to allow chaining of operations. - public static IImageProcessingContext BinaryDither(this IImageProcessingContext source, IOrderedDither dither, TPixel upperColor, TPixel lowerColor, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new BinaryOrderedDitherProcessor(dither, upperColor, lowerColor), rectangle); + public static IImageProcessingContext BinaryDither( + this IImageProcessingContext source, + IOrderedDither dither, + Color upperColor, + Color lowerColor, + Rectangle rectangle) => + source.ApplyProcessor(new BinaryOrderedDitherProcessor(dither, upperColor, lowerColor), rectangle); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/BinaryThresholdExtensions.cs b/src/ImageSharp/Processing/BinaryThresholdExtensions.cs index aecb784848..35aa681e33 100644 --- a/src/ImageSharp/Processing/BinaryThresholdExtensions.cs +++ b/src/ImageSharp/Processing/BinaryThresholdExtensions.cs @@ -1,14 +1,13 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Binarization; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Defines extension methods to apply binary thresholding on an + /// Defines extension methods to apply binary thresholding on an /// using Mutate/Clone. /// public static class BinaryThresholdExtensions @@ -16,45 +15,45 @@ namespace SixLabors.ImageSharp.Processing /// /// Applies binarization to the image splitting the pixels at the given threshold. /// - /// The pixel format. /// The image this method extends. /// The threshold to apply binarization of the image. Must be between 0 and 1. - /// The to allow chaining of operations. - public static IImageProcessingContext BinaryThreshold(this IImageProcessingContext source, float threshold) - where TPixel : struct, IPixel - => source.ApplyProcessor(new BinaryThresholdProcessor(threshold)); + /// The to allow chaining of operations. + public static IImageProcessingContext BinaryThreshold(this IImageProcessingContext source, float threshold) => + source.ApplyProcessor(new BinaryThresholdProcessor(threshold)); /// /// Applies binarization to the image splitting the pixels at the given threshold. /// - /// The pixel format. /// The image this method extends. /// The threshold to apply binarization of the image. Must be between 0 and 1. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. - public static IImageProcessingContext BinaryThreshold(this IImageProcessingContext source, float threshold, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new BinaryThresholdProcessor(threshold), rectangle); + /// The to allow chaining of operations. + public static IImageProcessingContext BinaryThreshold( + this IImageProcessingContext source, + float threshold, + Rectangle rectangle) => + source.ApplyProcessor(new BinaryThresholdProcessor(threshold), rectangle); /// /// Applies binarization to the image splitting the pixels at the given threshold. /// - /// The pixel format. /// The image this method extends. /// The threshold to apply binarization of the image. Must be between 0 and 1. /// The color to use for pixels that are above the threshold. /// The color to use for pixels that are below the threshold - /// The to allow chaining of operations. - public static IImageProcessingContext BinaryThreshold(this IImageProcessingContext source, float threshold, TPixel upperColor, TPixel lowerColor) - where TPixel : struct, IPixel - => source.ApplyProcessor(new BinaryThresholdProcessor(threshold, upperColor, lowerColor)); + /// The to allow chaining of operations. + public static IImageProcessingContext BinaryThreshold( + this IImageProcessingContext source, + float threshold, + Color upperColor, + Color lowerColor) => + source.ApplyProcessor(new BinaryThresholdProcessor(threshold, upperColor, lowerColor)); /// /// Applies binarization to the image splitting the pixels at the given threshold. /// - /// The pixel format. /// The image this method extends. /// The threshold to apply binarization of the image. Must be between 0 and 1. /// The color to use for pixels that are above the threshold. @@ -62,9 +61,13 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. - public static IImageProcessingContext BinaryThreshold(this IImageProcessingContext source, float threshold, TPixel upperColor, TPixel lowerColor, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new BinaryThresholdProcessor(threshold, upperColor, lowerColor), rectangle); + /// The to allow chaining of operations. + public static IImageProcessingContext BinaryThreshold( + this IImageProcessingContext source, + float threshold, + Color upperColor, + Color lowerColor, + Rectangle rectangle) => + source.ApplyProcessor(new BinaryThresholdProcessor(threshold, upperColor, lowerColor), rectangle); } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor.cs index 32cc2f434b..8129836641 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor.cs @@ -2,22 +2,19 @@ // Licensed under the Apache License, Version 2.0. using System; -using SixLabors.ImageSharp.Advanced; + using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Dithering; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Binarization { /// /// Performs binary threshold filtering against an image using error diffusion. /// - /// The pixel format. - internal class BinaryErrorDiffusionProcessor : ImageProcessor - where TPixel : struct, IPixel + public class BinaryErrorDiffusionProcessor : IImageProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The error diffuser public BinaryErrorDiffusionProcessor(IErrorDiffuser diffuser) @@ -26,23 +23,23 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The error diffuser /// The threshold to split the image. Must be between 0 and 1. public BinaryErrorDiffusionProcessor(IErrorDiffuser diffuser, float threshold) - : this(diffuser, threshold, NamedColors.White, NamedColors.Black) + : this(diffuser, threshold, Color.White, Color.Black) { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The error diffuser /// The threshold to split the image. Must be between 0 and 1. /// The color to use for pixels that are above the threshold. /// The color to use for pixels that are below the threshold. - public BinaryErrorDiffusionProcessor(IErrorDiffuser diffuser, float threshold, TPixel upperColor, TPixel lowerColor) + public BinaryErrorDiffusionProcessor(IErrorDiffuser diffuser, float threshold, Color upperColor, Color lowerColor) { Guard.NotNull(diffuser, nameof(diffuser)); Guard.MustBeBetweenOrEqualTo(threshold, 0, 1, nameof(threshold)); @@ -66,57 +63,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization /// /// Gets the color to use for pixels that are above the threshold. /// - public TPixel UpperColor { get; } + public Color UpperColor { get; } /// /// Gets the color to use for pixels that fall below the threshold. /// - public TPixel LowerColor { get; } + public Color LowerColor { get; } - /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + /// + public IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel { - byte threshold = (byte)MathF.Round(this.Threshold * 255F); - bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8); - - var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); - int startY = interest.Y; - int endY = interest.Bottom; - int startX = interest.X; - int endX = interest.Right; - - // Collect the values before looping so we can reduce our calculation count for identical sibling pixels - TPixel sourcePixel = source[startX, startY]; - TPixel previousPixel = sourcePixel; - Rgba32 rgba = default; - sourcePixel.ToRgba32(ref rgba); - - // Convert to grayscale using ITU-R Recommendation BT.709 if required - byte luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); - - for (int y = startY; y < endY; y++) - { - Span row = source.GetPixelRowSpan(y); - - for (int x = startX; x < endX; x++) - { - sourcePixel = row[x]; - - // Check if this is the same as the last pixel. If so use that value - // rather than calculating it again. This is an inexpensive optimization. - if (!previousPixel.Equals(sourcePixel)) - { - sourcePixel.ToRgba32(ref rgba); - luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); - - // Setup the previous pointer - previousPixel = sourcePixel; - } - - TPixel transformedPixel = luminance >= threshold ? this.UpperColor : this.LowerColor; - this.Diffuser.Dither(source, sourcePixel, transformedPixel, x, y, startX, startY, endX, endY); - } - } + return new BinaryErrorDiffusionProcessor(this); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor{TPixel}.cs new file mode 100644 index 0000000000..e3828aeb60 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor{TPixel}.cs @@ -0,0 +1,77 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors.Dithering; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Binarization +{ + /// + /// Performs binary threshold filtering against an image using error diffusion. + /// + /// The pixel format. + internal class BinaryErrorDiffusionProcessor : ImageProcessor + where TPixel : struct, IPixel + { + private readonly BinaryErrorDiffusionProcessor definition; + + public BinaryErrorDiffusionProcessor(BinaryErrorDiffusionProcessor definition) + { + this.definition = definition; + } + + /// + protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + TPixel upperColor = this.definition.UpperColor.ToPixel(); + TPixel lowerColor = this.definition.LowerColor.ToPixel(); + IErrorDiffuser diffuser = this.definition.Diffuser; + + byte threshold = (byte)MathF.Round(this.definition.Threshold * 255F); + bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8); + + var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); + int startY = interest.Y; + int endY = interest.Bottom; + int startX = interest.X; + int endX = interest.Right; + + // Collect the values before looping so we can reduce our calculation count for identical sibling pixels + TPixel sourcePixel = source[startX, startY]; + TPixel previousPixel = sourcePixel; + Rgba32 rgba = default; + sourcePixel.ToRgba32(ref rgba); + + // Convert to grayscale using ITU-R Recommendation BT.709 if required + byte luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); + + for (int y = startY; y < endY; y++) + { + Span row = source.GetPixelRowSpan(y); + + for (int x = startX; x < endX; x++) + { + sourcePixel = row[x]; + + // Check if this is the same as the last pixel. If so use that value + // rather than calculating it again. This is an inexpensive optimization. + if (!previousPixel.Equals(sourcePixel)) + { + sourcePixel.ToRgba32(ref rgba); + luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); + + // Setup the previous pointer + previousPixel = sourcePixel; + } + + TPixel transformedPixel = luminance >= threshold ? upperColor : lowerColor; + diffuser.Dither(source, sourcePixel, transformedPixel, x, y, startX, startY, endX, endY); + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor.cs index cfdaf107c3..99c57171cf 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor.cs @@ -2,36 +2,30 @@ // Licensed under the Apache License, Version 2.0. using System; -using SixLabors.ImageSharp.Advanced; + using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Dithering; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Binarization { - /// - /// Performs binary threshold filtering against an image using ordered dithering. - /// - /// The pixel format. - internal class BinaryOrderedDitherProcessor : ImageProcessor - where TPixel : struct, IPixel + public class BinaryOrderedDitherProcessor : IImageProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The ordered ditherer. public BinaryOrderedDitherProcessor(IOrderedDither dither) - : this(dither, NamedColors.White, NamedColors.Black) + : this(dither, Color.White, Color.Black) { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The ordered ditherer. /// The color to use for pixels that are above the threshold. /// The color to use for pixels that are below the threshold. - public BinaryOrderedDitherProcessor(IOrderedDither dither, TPixel upperColor, TPixel lowerColor) + public BinaryOrderedDitherProcessor(IOrderedDither dither, Color upperColor, Color lowerColor) { this.Dither = dither ?? throw new ArgumentNullException(nameof(dither)); this.UpperColor = upperColor; @@ -46,55 +40,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization /// /// Gets the color to use for pixels that are above the threshold. /// - public TPixel UpperColor { get; } + public Color UpperColor { get; } /// /// Gets the color to use for pixels that fall below the threshold. /// - public TPixel LowerColor { get; } + public Color LowerColor { get; } - /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + /// + public IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel { - bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8); - - var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); - int startY = interest.Y; - int endY = interest.Bottom; - int startX = interest.X; - int endX = interest.Right; - - // Collect the values before looping so we can reduce our calculation count for identical sibling pixels - TPixel sourcePixel = source[startX, startY]; - TPixel previousPixel = sourcePixel; - Rgba32 rgba = default; - sourcePixel.ToRgba32(ref rgba); - - // Convert to grayscale using ITU-R Recommendation BT.709 if required - byte luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); - - for (int y = startY; y < endY; y++) - { - Span row = source.GetPixelRowSpan(y); - - for (int x = startX; x < endX; x++) - { - sourcePixel = row[x]; - - // Check if this is the same as the last pixel. If so use that value - // rather than calculating it again. This is an inexpensive optimization. - if (!previousPixel.Equals(sourcePixel)) - { - sourcePixel.ToRgba32(ref rgba); - luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); - - // Setup the previous pointer - previousPixel = sourcePixel; - } - - this.Dither.Dither(source, sourcePixel, this.UpperColor, this.LowerColor, luminance, x, y); - } - } + return new BinaryOrderedDitherProcessor(this); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor{TPixel}.cs new file mode 100644 index 0000000000..b3d174dfbe --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor{TPixel}.cs @@ -0,0 +1,75 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors.Dithering; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Binarization +{ + /// + /// Performs binary threshold filtering against an image using ordered dithering. + /// + /// The pixel format. + internal class BinaryOrderedDitherProcessor : ImageProcessor + where TPixel : struct, IPixel + { + private readonly BinaryOrderedDitherProcessor definition; + + public BinaryOrderedDitherProcessor(BinaryOrderedDitherProcessor definition) + { + this.definition = definition; + } + + /// + protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + IOrderedDither dither = this.definition.Dither; + TPixel upperColor = this.definition.UpperColor.ToPixel(); + TPixel lowerColor = this.definition.LowerColor.ToPixel(); + + bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8); + + var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); + int startY = interest.Y; + int endY = interest.Bottom; + int startX = interest.X; + int endX = interest.Right; + + // Collect the values before looping so we can reduce our calculation count for identical sibling pixels + TPixel sourcePixel = source[startX, startY]; + TPixel previousPixel = sourcePixel; + Rgba32 rgba = default; + sourcePixel.ToRgba32(ref rgba); + + // Convert to grayscale using ITU-R Recommendation BT.709 if required + byte luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); + + for (int y = startY; y < endY; y++) + { + Span row = source.GetPixelRowSpan(y); + + for (int x = startX; x < endX; x++) + { + sourcePixel = row[x]; + + // Check if this is the same as the last pixel. If so use that value + // rather than calculating it again. This is an inexpensive optimization. + if (!previousPixel.Equals(sourcePixel)) + { + sourcePixel.ToRgba32(ref rgba); + luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); + + // Setup the previous pointer + previousPixel = sourcePixel; + } + + dither.Dither(source, sourcePixel, upperColor, lowerColor, luminance, x, y); + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs index 67dcfc7f1b..50086936cd 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs @@ -3,36 +3,31 @@ using System; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Binarization { /// /// Performs simple binary threshold filtering against an image. /// - /// The pixel format. - internal class BinaryThresholdProcessor : ImageProcessor - where TPixel : struct, IPixel + public class BinaryThresholdProcessor : IImageProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The threshold to split the image. Must be between 0 and 1. public BinaryThresholdProcessor(float threshold) - : this(threshold, NamedColors.White, NamedColors.Black) + : this(threshold, Color.White, Color.Black) { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The threshold to split the image. Must be between 0 and 1. /// The color to use for pixels that are above the threshold. /// The color to use for pixels that are below the threshold. - public BinaryThresholdProcessor(float threshold, TPixel upperColor, TPixel lowerColor) + public BinaryThresholdProcessor(float threshold, Color upperColor, Color lowerColor) { Guard.MustBeBetweenOrEqualTo(threshold, 0, 1, nameof(threshold)); this.Threshold = threshold; @@ -48,54 +43,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization /// /// Gets or sets the color to use for pixels that are above the threshold. /// - public TPixel UpperColor { get; set; } + public Color UpperColor { get; } /// /// Gets or sets the color to use for pixels that fall below the threshold. /// - public TPixel LowerColor { get; set; } + public Color LowerColor { get; } - /// - protected override void OnFrameApply( - ImageFrame source, - Rectangle sourceRectangle, - Configuration configuration) + /// + public IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel { - byte threshold = (byte)MathF.Round(this.Threshold * 255F); - TPixel upper = this.UpperColor; - TPixel lower = this.LowerColor; - - var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); - int startY = interest.Y; - int endY = interest.Bottom; - int startX = interest.X; - int endX = interest.Right; - - bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8); - - var workingRect = Rectangle.FromLTRB(startX, startY, endX, endY); - - ParallelHelper.IterateRows( - workingRect, - configuration, - rows => - { - Rgba32 rgba = default; - for (int y = rows.Min; y < rows.Max; y++) - { - Span row = source.GetPixelRowSpan(y); - - for (int x = startX; x < endX; x++) - { - ref TPixel color = ref row[x]; - color.ToRgba32(ref rgba); - - // Convert to grayscale using ITU-R Recommendation BT.709 if required - byte luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); - color = luminance >= threshold ? upper : lower; - } - } - }); + return new BinaryThresholdProcessor(this); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs new file mode 100644 index 0000000000..0d90d3647d --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Binarization +{ + /// + /// Performs simple binary threshold filtering against an image. + /// + /// The pixel format. + internal class BinaryThresholdProcessor : ImageProcessor + where TPixel : struct, IPixel + { + private readonly BinaryThresholdProcessor definition; + + public BinaryThresholdProcessor(BinaryThresholdProcessor definition) + { + this.definition = definition; + } + + /// + protected override void OnFrameApply( + ImageFrame source, + Rectangle sourceRectangle, + Configuration configuration) + { + byte threshold = (byte)MathF.Round(this.definition.Threshold * 255F); + TPixel upper = this.definition.UpperColor.ToPixel(); + TPixel lower = this.definition.LowerColor.ToPixel(); + + var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); + int startY = interest.Y; + int endY = interest.Bottom; + int startX = interest.X; + int endX = interest.Right; + + bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8); + + var workingRect = Rectangle.FromLTRB(startX, startY, endX, endY); + + ParallelHelper.IterateRows( + workingRect, + configuration, + rows => + { + Rgba32 rgba = default; + for (int y = rows.Min; y < rows.Max; y++) + { + Span row = source.GetPixelRowSpan(y); + + for (int x = startX; x < endX; x++) + { + ref TPixel color = ref row[x]; + color.ToRgba32(ref rgba); + + // Convert to grayscale using ITU-R Recommendation BT.709 if required + byte luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); + color = luminance >= threshold ? upper : lower; + } + } + }); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs index 5daf14fc32..f6771288fe 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs @@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { if (this.Grayscale) { - new GrayscaleBt709Processor(1F).ApplyToFrame(source, sourceRectangle, configuration); + new GrayscaleBt709Processor(1F).Apply(source, sourceRectangle, configuration); } } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs index 227003195d..5995ac844f 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs @@ -42,7 +42,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { if (this.Grayscale) { - new GrayscaleBt709Processor(1F).ApplyToFrame(source, sourceRectangle, configuration); + new GrayscaleBt709Processor(1F).Apply(source, sourceRectangle, configuration); } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs index 026313cc12..041c548032 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs @@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { if (this.Grayscale) { - new GrayscaleBt709Processor(1F).ApplyToFrame(source, sourceRectangle, configuration); + new GrayscaleBt709Processor(1F).Apply(source, sourceRectangle, configuration); } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/SobelProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/SobelProcessor.cs index bc4339e052..3d5d1e7bf1 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/SobelProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/SobelProcessor.cs @@ -26,13 +26,5 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { return new EdgeDetector2DProcessor(SobelKernels.SobelX, SobelKernels.SobelY, this.Grayscale); } - - // TODO: Move this to an appropriate extension method if possible. - internal void ApplyToFrame(ImageFrame frame, Rectangle sourceRectangle, Configuration configuration) - where TPixel : struct, IPixel - { - var processorImpl = new EdgeDetector2DProcessor(SobelKernels.SobelX, SobelKernels.SobelY, this.Grayscale); - processorImpl.Apply(frame, sourceRectangle, configuration); - } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs b/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs index 2d7d2a1eaa..ae6d5f6f79 100644 --- a/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs @@ -25,13 +25,5 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// Gets the proportion of the conversion. /// public float Amount { get; } - - // TODO: Move this to an appropriate extension method if possible. - internal void ApplyToFrame(ImageFrame frame, Rectangle sourceRectangle, Configuration configuration) - where TPixel : struct, IPixel - { - var processorImpl = new FilterProcessor(new GrayscaleBt709Processor(1F)); - processorImpl.Apply(frame, sourceRectangle, configuration); - } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs b/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs index 762d761c6c..91b2b30d02 100644 --- a/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs +++ b/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs @@ -14,6 +14,21 @@ namespace SixLabors.ImageSharp.Processing.Processors source.AcceptVisitor(visitor); } + /// + /// Apply an to a frame. + /// Only works from processors implemented by an subclass. + /// + internal static void Apply( + this IImageProcessor processor, + ImageFrame frame, + Rectangle sourceRectangle, + Configuration configuration) + where TPixel : struct, IPixel + { + var processorImpl = (ImageProcessor)processor.CreatePixelSpecificProcessor(); + processorImpl.Apply(frame, sourceRectangle, configuration); + } + private class ApplyVisitor : IImageVisitor { private readonly IImageProcessor processor; diff --git a/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs index eaeb6939ec..4bfeb25198 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs @@ -38,10 +38,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms Configuration configuration = source.GetConfiguration(); // Detect the edges. - new SobelProcessor(false).ApplyToFrame(temp, sourceRectangle, configuration); + new SobelProcessor(false).Apply(temp, sourceRectangle, configuration); // Apply threshold binarization filter. - new BinaryThresholdProcessor(this.definition.Threshold).Apply(temp, sourceRectangle, configuration); + new BinaryThresholdProcessor(this.definition.Threshold).Apply(temp, sourceRectangle, configuration); // Search for the first white pixels rectangle = ImageMaths.GetFilteredBoundingRectangle(temp, 0); diff --git a/src/ImageSharp/Source/ImageSharp/Color.NamedColors.cs b/src/ImageSharp/Source/ImageSharp/Color.NamedColors.cs new file mode 100644 index 0000000000..e00329d89a --- /dev/null +++ b/src/ImageSharp/Source/ImageSharp/Color.NamedColors.cs @@ -0,0 +1,717 @@ +// // Copyright (c) Six Labors and contributors. +// // Licensed under the Apache License, Version 2.0. +namespace SixLabors.ImageSharp +{ + public readonly partial struct Color + { + /// + /// Represents a matching the W3C definition that has an hex value of #F0F8FF. + /// + public static readonly Color AliceBlue = FromRgba(240, 248, 255, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FAEBD7. + /// + public static readonly Color AntiqueWhite = FromRgba(250, 235, 215, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #00FFFF. + /// + public static readonly Color Aqua = FromRgba(0, 255, 255, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #7FFFD4. + /// + public static readonly Color Aquamarine = FromRgba(127, 255, 212, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #F0FFFF. + /// + public static readonly Color Azure = FromRgba(240, 255, 255, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #F5F5DC. + /// + public static readonly Color Beige = FromRgba(245, 245, 220, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFE4C4. + /// + public static readonly Color Bisque = FromRgba(255, 228, 196, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #000000. + /// + public static readonly Color Black = FromRgba(0, 0, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFEBCD. + /// + public static readonly Color BlanchedAlmond = FromRgba(255, 235, 205, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #0000FF. + /// + public static readonly Color Blue = FromRgba(0, 0, 255, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #8A2BE2. + /// + public static readonly Color BlueViolet = FromRgba(138, 43, 226, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #A52A2A. + /// + public static readonly Color Brown = FromRgba(165, 42, 42, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #DEB887. + /// + public static readonly Color BurlyWood = FromRgba(222, 184, 135, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #5F9EA0. + /// + public static readonly Color CadetBlue = FromRgba(95, 158, 160, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #7FFF00. + /// + public static readonly Color Chartreuse = FromRgba(127, 255, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #D2691E. + /// + public static readonly Color Chocolate = FromRgba(210, 105, 30, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FF7F50. + /// + public static readonly Color Coral = FromRgba(255, 127, 80, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #6495ED. + /// + public static readonly Color CornflowerBlue = FromRgba(100, 149, 237, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFF8DC. + /// + public static readonly Color Cornsilk = FromRgba(255, 248, 220, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #DC143C. + /// + public static readonly Color Crimson = FromRgba(220, 20, 60, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #00FFFF. + /// + public static readonly Color Cyan = FromRgba(0, 255, 255, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #00008B. + /// + public static readonly Color DarkBlue = FromRgba(0, 0, 139, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #008B8B. + /// + public static readonly Color DarkCyan = FromRgba(0, 139, 139, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #B8860B. + /// + public static readonly Color DarkGoldenrod = FromRgba(184, 134, 11, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #A9A9A9. + /// + public static readonly Color DarkGray = FromRgba(169, 169, 169, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #006400. + /// + public static readonly Color DarkGreen = FromRgba(0, 100, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #BDB76B. + /// + public static readonly Color DarkKhaki = FromRgba(189, 183, 107, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #8B008B. + /// + public static readonly Color DarkMagenta = FromRgba(139, 0, 139, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #556B2F. + /// + public static readonly Color DarkOliveGreen = FromRgba(85, 107, 47, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FF8C00. + /// + public static readonly Color DarkOrange = FromRgba(255, 140, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #9932CC. + /// + public static readonly Color DarkOrchid = FromRgba(153, 50, 204, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #8B0000. + /// + public static readonly Color DarkRed = FromRgba(139, 0, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #E9967A. + /// + public static readonly Color DarkSalmon = FromRgba(233, 150, 122, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #8FBC8B. + /// + public static readonly Color DarkSeaGreen = FromRgba(143, 188, 139, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #483D8B. + /// + public static readonly Color DarkSlateBlue = FromRgba(72, 61, 139, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #2F4F4F. + /// + public static readonly Color DarkSlateGray = FromRgba(47, 79, 79, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #00CED1. + /// + public static readonly Color DarkTurquoise = FromRgba(0, 206, 209, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #9400D3. + /// + public static readonly Color DarkViolet = FromRgba(148, 0, 211, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FF1493. + /// + public static readonly Color DeepPink = FromRgba(255, 20, 147, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #00BFFF. + /// + public static readonly Color DeepSkyBlue = FromRgba(0, 191, 255, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #696969. + /// + public static readonly Color DimGray = FromRgba(105, 105, 105, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #1E90FF. + /// + public static readonly Color DodgerBlue = FromRgba(30, 144, 255, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #B22222. + /// + public static readonly Color Firebrick = FromRgba(178, 34, 34, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFAF0. + /// + public static readonly Color FloralWhite = FromRgba(255, 250, 240, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #228B22. + /// + public static readonly Color ForestGreen = FromRgba(34, 139, 34, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FF00FF. + /// + public static readonly Color Fuchsia = FromRgba(255, 0, 255, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #DCDCDC. + /// + public static readonly Color Gainsboro = FromRgba(220, 220, 220, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #F8F8FF. + /// + public static readonly Color GhostWhite = FromRgba(248, 248, 255, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFD700. + /// + public static readonly Color Gold = FromRgba(255, 215, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #DAA520. + /// + public static readonly Color Goldenrod = FromRgba(218, 165, 32, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #808080. + /// + public static readonly Color Gray = FromRgba(128, 128, 128, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #008000. + /// + public static readonly Color Green = FromRgba(0, 128, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #ADFF2F. + /// + public static readonly Color GreenYellow = FromRgba(173, 255, 47, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #F0FFF0. + /// + public static readonly Color Honeydew = FromRgba(240, 255, 240, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FF69B4. + /// + public static readonly Color HotPink = FromRgba(255, 105, 180, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #CD5C5C. + /// + public static readonly Color IndianRed = FromRgba(205, 92, 92, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #4B0082. + /// + public static readonly Color Indigo = FromRgba(75, 0, 130, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFFF0. + /// + public static readonly Color Ivory = FromRgba(255, 255, 240, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #F0E68C. + /// + public static readonly Color Khaki = FromRgba(240, 230, 140, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #E6E6FA. + /// + public static readonly Color Lavender = FromRgba(230, 230, 250, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFF0F5. + /// + public static readonly Color LavenderBlush = FromRgba(255, 240, 245, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #7CFC00. + /// + public static readonly Color LawnGreen = FromRgba(124, 252, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFACD. + /// + public static readonly Color LemonChiffon = FromRgba(255, 250, 205, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #ADD8E6. + /// + public static readonly Color LightBlue = FromRgba(173, 216, 230, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #F08080. + /// + public static readonly Color LightCoral = FromRgba(240, 128, 128, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #E0FFFF. + /// + public static readonly Color LightCyan = FromRgba(224, 255, 255, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FAFAD2. + /// + public static readonly Color LightGoldenrodYellow = FromRgba(250, 250, 210, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #D3D3D3. + /// + public static readonly Color LightGray = FromRgba(211, 211, 211, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #90EE90. + /// + public static readonly Color LightGreen = FromRgba(144, 238, 144, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFB6C1. + /// + public static readonly Color LightPink = FromRgba(255, 182, 193, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFA07A. + /// + public static readonly Color LightSalmon = FromRgba(255, 160, 122, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #20B2AA. + /// + public static readonly Color LightSeaGreen = FromRgba(32, 178, 170, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #87CEFA. + /// + public static readonly Color LightSkyBlue = FromRgba(135, 206, 250, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #778899. + /// + public static readonly Color LightSlateGray = FromRgba(119, 136, 153, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #B0C4DE. + /// + public static readonly Color LightSteelBlue = FromRgba(176, 196, 222, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFFE0. + /// + public static readonly Color LightYellow = FromRgba(255, 255, 224, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #00FF00. + /// + public static readonly Color Lime = FromRgba(0, 255, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #32CD32. + /// + public static readonly Color LimeGreen = FromRgba(50, 205, 50, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FAF0E6. + /// + public static readonly Color Linen = FromRgba(250, 240, 230, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FF00FF. + /// + public static readonly Color Magenta = FromRgba(255, 0, 255, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #800000. + /// + public static readonly Color Maroon = FromRgba(128, 0, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #66CDAA. + /// + public static readonly Color MediumAquamarine = FromRgba(102, 205, 170, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #0000CD. + /// + public static readonly Color MediumBlue = FromRgba(0, 0, 205, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #BA55D3. + /// + public static readonly Color MediumOrchid = FromRgba(186, 85, 211, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #9370DB. + /// + public static readonly Color MediumPurple = FromRgba(147, 112, 219, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #3CB371. + /// + public static readonly Color MediumSeaGreen = FromRgba(60, 179, 113, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #7B68EE. + /// + public static readonly Color MediumSlateBlue = FromRgba(123, 104, 238, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #00FA9A. + /// + public static readonly Color MediumSpringGreen = FromRgba(0, 250, 154, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #48D1CC. + /// + public static readonly Color MediumTurquoise = FromRgba(72, 209, 204, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #C71585. + /// + public static readonly Color MediumVioletRed = FromRgba(199, 21, 133, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #191970. + /// + public static readonly Color MidnightBlue = FromRgba(25, 25, 112, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #F5FFFA. + /// + public static readonly Color MintCream = FromRgba(245, 255, 250, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFE4E1. + /// + public static readonly Color MistyRose = FromRgba(255, 228, 225, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFE4B5. + /// + public static readonly Color Moccasin = FromRgba(255, 228, 181, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFDEAD. + /// + public static readonly Color NavajoWhite = FromRgba(255, 222, 173, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #000080. + /// + public static readonly Color Navy = FromRgba(0, 0, 128, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FDF5E6. + /// + public static readonly Color OldLace = FromRgba(253, 245, 230, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #808000. + /// + public static readonly Color Olive = FromRgba(128, 128, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #6B8E23. + /// + public static readonly Color OliveDrab = FromRgba(107, 142, 35, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFA500. + /// + public static readonly Color Orange = FromRgba(255, 165, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FF4500. + /// + public static readonly Color OrangeRed = FromRgba(255, 69, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #DA70D6. + /// + public static readonly Color Orchid = FromRgba(218, 112, 214, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #EEE8AA. + /// + public static readonly Color PaleGoldenrod = FromRgba(238, 232, 170, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #98FB98. + /// + public static readonly Color PaleGreen = FromRgba(152, 251, 152, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #AFEEEE. + /// + public static readonly Color PaleTurquoise = FromRgba(175, 238, 238, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #DB7093. + /// + public static readonly Color PaleVioletRed = FromRgba(219, 112, 147, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFEFD5. + /// + public static readonly Color PapayaWhip = FromRgba(255, 239, 213, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFDAB9. + /// + public static readonly Color PeachPuff = FromRgba(255, 218, 185, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #CD853F. + /// + public static readonly Color Peru = FromRgba(205, 133, 63, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFC0CB. + /// + public static readonly Color Pink = FromRgba(255, 192, 203, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #DDA0DD. + /// + public static readonly Color Plum = FromRgba(221, 160, 221, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #B0E0E6. + /// + public static readonly Color PowderBlue = FromRgba(176, 224, 230, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #800080. + /// + public static readonly Color Purple = FromRgba(128, 0, 128, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #663399. + /// + public static readonly Color RebeccaPurple = FromRgba(102, 51, 153, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FF0000. + /// + public static readonly Color Red = FromRgba(255, 0, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #BC8F8F. + /// + public static readonly Color RosyBrown = FromRgba(188, 143, 143, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #4169E1. + /// + public static readonly Color RoyalBlue = FromRgba(65, 105, 225, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #8B4513. + /// + public static readonly Color SaddleBrown = FromRgba(139, 69, 19, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FA8072. + /// + public static readonly Color Salmon = FromRgba(250, 128, 114, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #F4A460. + /// + public static readonly Color SandyBrown = FromRgba(244, 164, 96, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #2E8B57. + /// + public static readonly Color SeaGreen = FromRgba(46, 139, 87, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFF5EE. + /// + public static readonly Color SeaShell = FromRgba(255, 245, 238, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #A0522D. + /// + public static readonly Color Sienna = FromRgba(160, 82, 45, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #C0C0C0. + /// + public static readonly Color Silver = FromRgba(192, 192, 192, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #87CEEB. + /// + public static readonly Color SkyBlue = FromRgba(135, 206, 235, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #6A5ACD. + /// + public static readonly Color SlateBlue = FromRgba(106, 90, 205, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #708090. + /// + public static readonly Color SlateGray = FromRgba(112, 128, 144, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFAFA. + /// + public static readonly Color Snow = FromRgba(255, 250, 250, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #00FF7F. + /// + public static readonly Color SpringGreen = FromRgba(0, 255, 127, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #4682B4. + /// + public static readonly Color SteelBlue = FromRgba(70, 130, 180, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #D2B48C. + /// + public static readonly Color Tan = FromRgba(210, 180, 140, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #008080. + /// + public static readonly Color Teal = FromRgba(0, 128, 128, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #D8BFD8. + /// + public static readonly Color Thistle = FromRgba(216, 191, 216, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FF6347. + /// + public static readonly Color Tomato = FromRgba(255, 99, 71, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFFFF. + /// + public static readonly Color Transparent = FromRgba(255, 255, 255, 0); + + /// + /// Represents a matching the W3C definition that has an hex value of #40E0D0. + /// + public static readonly Color Turquoise = FromRgba(64, 224, 208, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #EE82EE. + /// + public static readonly Color Violet = FromRgba(238, 130, 238, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #F5DEB3. + /// + public static readonly Color Wheat = FromRgba(245, 222, 179, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFFFF. + /// + public static readonly Color White = FromRgba(255, 255, 255, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #F5F5F5. + /// + public static readonly Color WhiteSmoke = FromRgba(245, 245, 245, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFF00. + /// + public static readonly Color Yellow = FromRgba(255, 255, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #9ACD32. + /// + public static readonly Color YellowGreen = FromRgba(154, 205, 50, 255); + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Binarization/BinaryDitherTest.cs b/tests/ImageSharp.Tests/Processing/Binarization/BinaryDitherTest.cs index 5f6e825f63..dcbf0ca038 100644 --- a/tests/ImageSharp.Tests/Processing/Binarization/BinaryDitherTest.cs +++ b/tests/ImageSharp.Tests/Processing/Binarization/BinaryDitherTest.cs @@ -25,38 +25,38 @@ namespace SixLabors.ImageSharp.Tests.Processing.Binarization public void BinaryDither_CorrectProcessor() { this.operations.BinaryDither(this.orderedDither); - BinaryOrderedDitherProcessor p = this.Verify>(); + BinaryOrderedDitherProcessor p = this.Verify(); Assert.Equal(this.orderedDither, p.Dither); - Assert.Equal(NamedColors.White, p.UpperColor); - Assert.Equal(NamedColors.Black, p.LowerColor); + Assert.Equal(Color.White, p.UpperColor); + Assert.Equal(Color.Black, p.LowerColor); } [Fact] public void BinaryDither_rect_CorrectProcessor() { this.operations.BinaryDither(this.orderedDither, this.rect); - BinaryOrderedDitherProcessor p = this.Verify>(this.rect); + BinaryOrderedDitherProcessor p = this.Verify(this.rect); Assert.Equal(this.orderedDither, p.Dither); - Assert.Equal(NamedColors.White, p.UpperColor); - Assert.Equal(NamedColors.Black, p.LowerColor); + Assert.Equal(Color.White, p.UpperColor); + Assert.Equal(Color.Black, p.LowerColor); } [Fact] public void BinaryDither_index_CorrectProcessor() { - this.operations.BinaryDither(this.orderedDither, NamedColors.Yellow, NamedColors.HotPink); - BinaryOrderedDitherProcessor p = this.Verify>(); + this.operations.BinaryDither(this.orderedDither, Color.Yellow, Color.HotPink); + BinaryOrderedDitherProcessor p = this.Verify(); Assert.Equal(this.orderedDither, p.Dither); - Assert.Equal(NamedColors.Yellow, p.UpperColor); - Assert.Equal(NamedColors.HotPink, p.LowerColor); + Assert.Equal(Color.Yellow, p.UpperColor); + Assert.Equal(Color.HotPink, p.LowerColor); } [Fact] public void BinaryDither_index_rect_CorrectProcessor() { - this.operations.BinaryDither(this.orderedDither, NamedColors.Yellow, NamedColors.HotPink, this.rect); - BinaryOrderedDitherProcessor p = this.Verify>(this.rect); + this.operations.BinaryDither(this.orderedDither, Color.Yellow, Color.HotPink, this.rect); + BinaryOrderedDitherProcessor p = this.Verify(this.rect); Assert.Equal(this.orderedDither, p.Dither); - Assert.Equal(NamedColors.HotPink, p.LowerColor); + Assert.Equal(Color.HotPink, p.LowerColor); } @@ -64,44 +64,44 @@ namespace SixLabors.ImageSharp.Tests.Processing.Binarization public void BinaryDither_ErrorDiffuser_CorrectProcessor() { this.operations.BinaryDiffuse(this.errorDiffuser, .4F); - BinaryErrorDiffusionProcessor p = this.Verify>(); + BinaryErrorDiffusionProcessor p = this.Verify(); Assert.Equal(this.errorDiffuser, p.Diffuser); Assert.Equal(.4F, p.Threshold); - Assert.Equal(NamedColors.White, p.UpperColor); - Assert.Equal(NamedColors.Black, p.LowerColor); + Assert.Equal(Color.White, p.UpperColor); + Assert.Equal(Color.Black, p.LowerColor); } [Fact] public void BinaryDither_ErrorDiffuser_rect_CorrectProcessor() { this.operations.BinaryDiffuse(this.errorDiffuser, .3F, this.rect); - BinaryErrorDiffusionProcessor p = this.Verify>(this.rect); + BinaryErrorDiffusionProcessor p = this.Verify(this.rect); Assert.Equal(this.errorDiffuser, p.Diffuser); Assert.Equal(.3F, p.Threshold); - Assert.Equal(NamedColors.White, p.UpperColor); - Assert.Equal(NamedColors.Black, p.LowerColor); + Assert.Equal(Color.White, p.UpperColor); + Assert.Equal(Color.Black, p.LowerColor); } [Fact] public void BinaryDither_ErrorDiffuser_CorrectProcessorWithColors() { - this.operations.BinaryDiffuse(this.errorDiffuser, .5F, NamedColors.HotPink, NamedColors.Yellow); - BinaryErrorDiffusionProcessor p = this.Verify>(); + this.operations.BinaryDiffuse(this.errorDiffuser, .5F, Color.HotPink, Color.Yellow); + BinaryErrorDiffusionProcessor p = this.Verify(); Assert.Equal(this.errorDiffuser, p.Diffuser); Assert.Equal(.5F, p.Threshold); - Assert.Equal(NamedColors.HotPink, p.UpperColor); - Assert.Equal(NamedColors.Yellow, p.LowerColor); + Assert.Equal(Color.HotPink, p.UpperColor); + Assert.Equal(Color.Yellow, p.LowerColor); } [Fact] public void BinaryDither_ErrorDiffuser_rect_CorrectProcessorWithColors() { - this.operations.BinaryDiffuse(this.errorDiffuser, .5F, NamedColors.HotPink, NamedColors.Yellow, this.rect); - BinaryErrorDiffusionProcessor p = this.Verify>(this.rect); + this.operations.BinaryDiffuse(this.errorDiffuser, .5F, Color.HotPink, Color.Yellow, this.rect); + BinaryErrorDiffusionProcessor p = this.Verify(this.rect); Assert.Equal(this.errorDiffuser, p.Diffuser); Assert.Equal(.5F, p.Threshold); - Assert.Equal(NamedColors.HotPink, p.UpperColor); - Assert.Equal(NamedColors.Yellow, p.LowerColor); + Assert.Equal(Color.HotPink, p.UpperColor); + Assert.Equal(Color.Yellow, p.LowerColor); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Binarization/BinaryThresholdTest.cs b/tests/ImageSharp.Tests/Processing/Binarization/BinaryThresholdTest.cs index 569c4ba217..f0fcba1815 100644 --- a/tests/ImageSharp.Tests/Processing/Binarization/BinaryThresholdTest.cs +++ b/tests/ImageSharp.Tests/Processing/Binarization/BinaryThresholdTest.cs @@ -15,40 +15,40 @@ namespace SixLabors.ImageSharp.Tests.Processing.Binarization public void BinaryThreshold_CorrectProcessor() { this.operations.BinaryThreshold(.23f); - BinaryThresholdProcessor p = this.Verify>(); + BinaryThresholdProcessor p = this.Verify(); Assert.Equal(.23f, p.Threshold); - Assert.Equal(NamedColors.White, p.UpperColor); - Assert.Equal(NamedColors.Black, p.LowerColor); + Assert.Equal(Color.White, p.UpperColor); + Assert.Equal(Color.Black, p.LowerColor); } [Fact] public void BinaryThreshold_rect_CorrectProcessor() { this.operations.BinaryThreshold(.93f, this.rect); - BinaryThresholdProcessor p = this.Verify>(this.rect); + BinaryThresholdProcessor p = this.Verify(this.rect); Assert.Equal(.93f, p.Threshold); - Assert.Equal(NamedColors.White, p.UpperColor); - Assert.Equal(NamedColors.Black, p.LowerColor); + Assert.Equal(Color.White, p.UpperColor); + Assert.Equal(Color.Black, p.LowerColor); } [Fact] public void BinaryThreshold_CorrectProcessorWithUpperLower() { - this.operations.BinaryThreshold(.23f, NamedColors.HotPink, NamedColors.Yellow); - BinaryThresholdProcessor p = this.Verify>(); + this.operations.BinaryThreshold(.23f, Color.HotPink, Color.Yellow); + BinaryThresholdProcessor p = this.Verify(); Assert.Equal(.23f, p.Threshold); - Assert.Equal(NamedColors.HotPink, p.UpperColor); - Assert.Equal(NamedColors.Yellow, p.LowerColor); + Assert.Equal(Color.HotPink, p.UpperColor); + Assert.Equal(Color.Yellow, p.LowerColor); } [Fact] public void BinaryThreshold_rect_CorrectProcessorWithUpperLower() { - this.operations.BinaryThreshold(.93f, NamedColors.HotPink, NamedColors.Yellow, this.rect); - BinaryThresholdProcessor p = this.Verify>(this.rect); + this.operations.BinaryThreshold(.93f, Color.HotPink, Color.Yellow, this.rect); + BinaryThresholdProcessor p = this.Verify(this.rect); Assert.Equal(.93f, p.Threshold); - Assert.Equal(NamedColors.HotPink, p.UpperColor); - Assert.Equal(NamedColors.Yellow, p.LowerColor); + Assert.Equal(Color.HotPink, p.UpperColor); + Assert.Equal(Color.Yellow, p.LowerColor); } } } \ No newline at end of file From 422a78e9494c83496617227441ab1c0b0014c05e Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 6 May 2019 22:58:17 +0200 Subject: [PATCH 144/223] Implement WernerPalette and WebSafePalette for Color --- src/ImageSharp/Color.cs | 83 ------ .../ImageSharp => Color}/Color.NamedColors.cs | 1 + src/ImageSharp/Color/Color.WebSafePalette.cs | 163 +++++++++++ src/ImageSharp/Color/Color.WernerPalette.cs | 132 +++++++++ src/ImageSharp/Color/Color.cs | 185 ++++++++++++ src/ImageSharp/ImageSharp.csproj.DotSettings | 1 + .../Color/ColorTests.CastFrom.cs | 93 ++++++ .../Color/ColorTests.CastTo.cs | 93 ++++++ .../Color/ColorTests.ConstructFrom.cs | 93 ++++++ tests/ImageSharp.Tests/Color/ColorTests.cs | 97 ++++++ .../Color/ReferencePalette.cs | 277 ++++++++++++++++++ tests/ImageSharp.Tests/ColorTests.cs | 255 ---------------- 12 files changed, 1135 insertions(+), 338 deletions(-) delete mode 100644 src/ImageSharp/Color.cs rename src/ImageSharp/{Source/ImageSharp => Color}/Color.NamedColors.cs (99%) create mode 100644 src/ImageSharp/Color/Color.WebSafePalette.cs create mode 100644 src/ImageSharp/Color/Color.WernerPalette.cs create mode 100644 src/ImageSharp/Color/Color.cs create mode 100644 tests/ImageSharp.Tests/Color/ColorTests.CastFrom.cs create mode 100644 tests/ImageSharp.Tests/Color/ColorTests.CastTo.cs create mode 100644 tests/ImageSharp.Tests/Color/ColorTests.ConstructFrom.cs create mode 100644 tests/ImageSharp.Tests/Color/ColorTests.cs create mode 100644 tests/ImageSharp.Tests/Color/ReferencePalette.cs delete mode 100644 tests/ImageSharp.Tests/ColorTests.cs diff --git a/src/ImageSharp/Color.cs b/src/ImageSharp/Color.cs deleted file mode 100644 index 6dae76c34d..0000000000 --- a/src/ImageSharp/Color.cs +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Numerics; - -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp -{ - public readonly partial struct Color - { - private readonly Rgba64 data; - - public Color(Rgba64 pixel) - { - this.data = pixel; - } - - public Color(Rgba32 pixel) - { - this.data = new Rgba64(pixel); - } - - public Color(Argb32 pixel) - { - this.data = new Rgba64(pixel); - } - - public Color(Bgra32 pixel) - { - this.data = new Rgba64(pixel); - } - - public Color(Rgb24 pixel) - { - this.data = new Rgba64(pixel); - } - - public Color(Bgr24 pixel) - { - this.data = new Rgba64(pixel); - } - - public Color(Vector4 vector) - { - this.data = new Rgba64(vector); - } - - public static implicit operator Color(Rgba64 source) => new Color(source); - - public static implicit operator Color(Rgba32 source) => new Color(source); - - public static implicit operator Color(Bgra32 source) => new Color(source); - - public static implicit operator Color(Argb32 source) => new Color(source); - - public static implicit operator Color(Rgb24 source) => new Color(source); - - public static implicit operator Color(Bgr24 source) => new Color(source); - - public static implicit operator Rgba64(Color color) => color.data; - - public static implicit operator Rgba32(Color color) => color.data.ToRgba32(); - - public static implicit operator Bgra32(Color color) => color.data.ToBgra32(); - - public static implicit operator Argb32(Color color) => color.data.ToArgb32(); - - public static implicit operator Rgb24(Color color) => color.data.ToRgb24(); - - public static implicit operator Bgr24(Color color) => color.data.ToBgr24(); - - public static Color FromRgba(byte r, byte g, byte b, byte a) => new Color(new Rgba32(r, g, b, a)); - - public TPixel ToPixel() - where TPixel : struct, IPixel - { - TPixel pixel = default; - pixel.FromRgba64(this.data); - return pixel; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Source/ImageSharp/Color.NamedColors.cs b/src/ImageSharp/Color/Color.NamedColors.cs similarity index 99% rename from src/ImageSharp/Source/ImageSharp/Color.NamedColors.cs rename to src/ImageSharp/Color/Color.NamedColors.cs index e00329d89a..a712f85dab 100644 --- a/src/ImageSharp/Source/ImageSharp/Color.NamedColors.cs +++ b/src/ImageSharp/Color/Color.NamedColors.cs @@ -1,5 +1,6 @@ // // Copyright (c) Six Labors and contributors. // // Licensed under the Apache License, Version 2.0. + namespace SixLabors.ImageSharp { public readonly partial struct Color diff --git a/src/ImageSharp/Color/Color.WebSafePalette.cs b/src/ImageSharp/Color/Color.WebSafePalette.cs new file mode 100644 index 0000000000..13720ec493 --- /dev/null +++ b/src/ImageSharp/Color/Color.WebSafePalette.cs @@ -0,0 +1,163 @@ +// // Copyright (c) Six Labors and contributors. +// // Licensed under the Apache License, Version 2.0. + +using System; + +namespace SixLabors.ImageSharp +{ + public partial struct Color + { + private static readonly Lazy WebSafePaletteLazy = new Lazy(CreateWebSafePalette, true); + + /// + /// Gets a collection of named, web safe, colors as defined in the CSS Color Module Level 4. + /// + public static ReadOnlySpan WebSafePalette => WebSafePaletteLazy.Value; + + private static Color[] CreateWebSafePalette() => new[] + { + AliceBlue, + AntiqueWhite, + Aqua, + Aquamarine, + Azure, + Beige, + Bisque, + Black, + BlanchedAlmond, + Blue, + BlueViolet, + Brown, + BurlyWood, + CadetBlue, + Chartreuse, + Chocolate, + Coral, + CornflowerBlue, + Cornsilk, + Crimson, + Cyan, + DarkBlue, + DarkCyan, + DarkGoldenrod, + DarkGray, + DarkGreen, + DarkKhaki, + DarkMagenta, + DarkOliveGreen, + DarkOrange, + DarkOrchid, + DarkRed, + DarkSalmon, + DarkSeaGreen, + DarkSlateBlue, + DarkSlateGray, + DarkTurquoise, + DarkViolet, + DeepPink, + DeepSkyBlue, + DimGray, + DodgerBlue, + Firebrick, + FloralWhite, + ForestGreen, + Fuchsia, + Gainsboro, + GhostWhite, + Gold, + Goldenrod, + Gray, + Green, + GreenYellow, + Honeydew, + HotPink, + IndianRed, + Indigo, + Ivory, + Khaki, + Lavender, + LavenderBlush, + LawnGreen, + LemonChiffon, + LightBlue, + LightCoral, + LightCyan, + LightGoldenrodYellow, + LightGray, + LightGreen, + LightPink, + LightSalmon, + LightSeaGreen, + LightSkyBlue, + LightSlateGray, + LightSteelBlue, + LightYellow, + Lime, + LimeGreen, + Linen, + Magenta, + Maroon, + MediumAquamarine, + MediumBlue, + MediumOrchid, + MediumPurple, + MediumSeaGreen, + MediumSlateBlue, + MediumSpringGreen, + MediumTurquoise, + MediumVioletRed, + MidnightBlue, + MintCream, + MistyRose, + Moccasin, + NavajoWhite, + Navy, + OldLace, + Olive, + OliveDrab, + Orange, + OrangeRed, + Orchid, + PaleGoldenrod, + PaleGreen, + PaleTurquoise, + PaleVioletRed, + PapayaWhip, + PeachPuff, + Peru, + Pink, + Plum, + PowderBlue, + Purple, + RebeccaPurple, + Red, + RosyBrown, + RoyalBlue, + SaddleBrown, + Salmon, + SandyBrown, + SeaGreen, + SeaShell, + Sienna, + Silver, + SkyBlue, + SlateBlue, + SlateGray, + Snow, + SpringGreen, + SteelBlue, + Tan, + Teal, + Thistle, + Tomato, + Transparent, + Turquoise, + Violet, + Wheat, + White, + WhiteSmoke, + Yellow, + YellowGreen + }; + } +} \ No newline at end of file diff --git a/src/ImageSharp/Color/Color.WernerPalette.cs b/src/ImageSharp/Color/Color.WernerPalette.cs new file mode 100644 index 0000000000..45823479e1 --- /dev/null +++ b/src/ImageSharp/Color/Color.WernerPalette.cs @@ -0,0 +1,132 @@ +// // Copyright (c) Six Labors and contributors. +// // Licensed under the Apache License, Version 2.0. + +using System; + +namespace SixLabors.ImageSharp +{ + public partial struct Color + { + private static readonly Lazy WernerPaletteLazy = new Lazy(CreateWernerPalette, true); + + /// + /// Gets a collection of colors as defined in the original second edition of Werner’s Nomenclature of Colours 1821. + /// The hex codes were collected and defined by Nicholas Rougeux . + /// + public static ReadOnlySpan WernerPalette => WernerPaletteLazy.Value; + + private static Color[] CreateWernerPalette() => new[] + { + FromHex("#f1e9cd"), + FromHex("#f2e7cf"), + FromHex("#ece6d0"), + FromHex("#f2eacc"), + FromHex("#f3e9ca"), + FromHex("#f2ebcd"), + FromHex("#e6e1c9"), + FromHex("#e2ddc6"), + FromHex("#cbc8b7"), + FromHex("#bfbbb0"), + FromHex("#bebeb3"), + FromHex("#b7b5ac"), + FromHex("#bab191"), + FromHex("#9c9d9a"), + FromHex("#8a8d84"), + FromHex("#5b5c61"), + FromHex("#555152"), + FromHex("#413f44"), + FromHex("#454445"), + FromHex("#423937"), + FromHex("#433635"), + FromHex("#252024"), + FromHex("#241f20"), + FromHex("#281f3f"), + FromHex("#1c1949"), + FromHex("#4f638d"), + FromHex("#383867"), + FromHex("#5c6b8f"), + FromHex("#657abb"), + FromHex("#6f88af"), + FromHex("#7994b5"), + FromHex("#6fb5a8"), + FromHex("#719ba2"), + FromHex("#8aa1a6"), + FromHex("#d0d5d3"), + FromHex("#8590ae"), + FromHex("#3a2f52"), + FromHex("#39334a"), + FromHex("#6c6d94"), + FromHex("#584c77"), + FromHex("#533552"), + FromHex("#463759"), + FromHex("#bfbac0"), + FromHex("#77747f"), + FromHex("#4a475c"), + FromHex("#b8bfaf"), + FromHex("#b2b599"), + FromHex("#979c84"), + FromHex("#5d6161"), + FromHex("#61ac86"), + FromHex("#a4b6a7"), + FromHex("#adba98"), + FromHex("#93b778"), + FromHex("#7d8c55"), + FromHex("#33431e"), + FromHex("#7c8635"), + FromHex("#8e9849"), + FromHex("#c2c190"), + FromHex("#67765b"), + FromHex("#ab924b"), + FromHex("#c8c76f"), + FromHex("#ccc050"), + FromHex("#ebdd99"), + FromHex("#ab9649"), + FromHex("#dbc364"), + FromHex("#e6d058"), + FromHex("#ead665"), + FromHex("#d09b2c"), + FromHex("#a36629"), + FromHex("#a77d35"), + FromHex("#f0d696"), + FromHex("#d7c485"), + FromHex("#f1d28c"), + FromHex("#efcc83"), + FromHex("#f3daa7"), + FromHex("#dfa837"), + FromHex("#ebbc71"), + FromHex("#d17c3f"), + FromHex("#92462f"), + FromHex("#be7249"), + FromHex("#bb603c"), + FromHex("#c76b4a"), + FromHex("#a75536"), + FromHex("#b63e36"), + FromHex("#b5493a"), + FromHex("#cd6d57"), + FromHex("#711518"), + FromHex("#e9c49d"), + FromHex("#eedac3"), + FromHex("#eecfbf"), + FromHex("#ce536b"), + FromHex("#b74a70"), + FromHex("#b7757c"), + FromHex("#612741"), + FromHex("#7a4848"), + FromHex("#3f3033"), + FromHex("#8d746f"), + FromHex("#4d3635"), + FromHex("#6e3b31"), + FromHex("#864735"), + FromHex("#553d3a"), + FromHex("#613936"), + FromHex("#7a4b3a"), + FromHex("#946943"), + FromHex("#c39e6d"), + FromHex("#513e32"), + FromHex("#8b7859"), + FromHex("#9b856b"), + FromHex("#766051"), + FromHex("#453b32") + }; + } +} \ No newline at end of file diff --git a/src/ImageSharp/Color/Color.cs b/src/ImageSharp/Color/Color.cs new file mode 100644 index 0000000000..3e20cafbd3 --- /dev/null +++ b/src/ImageSharp/Color/Color.cs @@ -0,0 +1,185 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers.Binary; +using System.Globalization; +using System.Numerics; + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp +{ + public readonly partial struct Color : IEquatable + { + private readonly Rgba64 data; + + public Color(Rgba64 pixel) + { + this.data = pixel; + } + + public Color(Rgba32 pixel) + { + this.data = new Rgba64(pixel); + } + + public Color(Argb32 pixel) + { + this.data = new Rgba64(pixel); + } + + public Color(Bgra32 pixel) + { + this.data = new Rgba64(pixel); + } + + public Color(Rgb24 pixel) + { + this.data = new Rgba64(pixel); + } + + public Color(Bgr24 pixel) + { + this.data = new Rgba64(pixel); + } + + public Color(Vector4 vector) + { + this.data = new Rgba64(vector); + } + + public static implicit operator Color(Rgba64 source) => new Color(source); + + public static implicit operator Color(Rgba32 source) => new Color(source); + + public static implicit operator Color(Bgra32 source) => new Color(source); + + public static implicit operator Color(Argb32 source) => new Color(source); + + public static implicit operator Color(Rgb24 source) => new Color(source); + + public static implicit operator Color(Bgr24 source) => new Color(source); + + public static implicit operator Rgba64(Color color) => color.data; + + public static implicit operator Rgba32(Color color) => color.data.ToRgba32(); + + public static implicit operator Bgra32(Color color) => color.data.ToBgra32(); + + public static implicit operator Argb32(Color color) => color.data.ToArgb32(); + + public static implicit operator Rgb24(Color color) => color.data.ToRgb24(); + + public static implicit operator Bgr24(Color color) => color.data.ToBgr24(); + + public static bool operator ==(Color left, Color right) + { + return left.Equals(right); + } + + public static bool operator !=(Color left, Color right) + { + return !left.Equals(right); + } + + public static Color FromRgba(byte r, byte g, byte b, byte a) => new Color(new Rgba32(r, g, b, a)); + + /// + /// Creates a new instance from the string representing a color in hexadecimal form. + /// + /// + /// The hexadecimal representation of the combined color components arranged + /// in rgb, rgba, rrggbb, or rrggbbaa format to match web syntax. + /// + /// Returns a that represents the color defined by the provided RGBA hex string. + public static Color FromHex(string hex) + { + Guard.NotNullOrWhiteSpace(hex, nameof(hex)); + + hex = ToRgbaHex(hex); + + if (hex is null || !uint.TryParse(hex, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out uint packedValue)) + { + throw new ArgumentException("Hexadecimal string is not in the correct format.", nameof(hex)); + } + + var rgba = new Rgba32(BinaryPrimitives.ReverseEndianness(packedValue)); + return new Color(rgba); + } + + /// + /// Gets the hexadecimal representation of the color instance in rrggbbaa form. + /// + /// A hexadecimal string representation of the value. + public string ToHex() => this.data.ToRgba32().ToHex(); + + /// + public override string ToString() => this.ToHex(); + + /// + /// Converts the specified hex value to an rrggbbaa hex value. + /// + /// The hex value to convert. + /// + /// A rrggbbaa hex value. + /// + private static string ToRgbaHex(string hex) + { + if (hex[0] == '#') + { + hex = hex.Substring(1); + } + + if (hex.Length == 8) + { + return hex; + } + + if (hex.Length == 6) + { + return hex + "FF"; + } + + if (hex.Length < 3 || hex.Length > 4) + { + return null; + } + + char r = hex[0]; + char g = hex[1]; + char b = hex[2]; + char a = hex.Length == 3 ? 'F' : hex[3]; + + return new string(new[] { r, r, g, g, b, b, a, a }); + } + + public TPixel ToPixel() + where TPixel : struct, IPixel + { + TPixel pixel = default; + pixel.FromRgba64(this.data); + return pixel; + } + + public bool Equals(Color other) + { + return this.data.PackedValue == other.data.PackedValue; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + { + return false; + } + return obj is Color other && this.Equals(other); + } + + /// + public override int GetHashCode() + { + return this.data.PackedValue.GetHashCode(); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/ImageSharp.csproj.DotSettings b/src/ImageSharp/ImageSharp.csproj.DotSettings index e446893e94..e43e92ddce 100644 --- a/src/ImageSharp/ImageSharp.csproj.DotSettings +++ b/src/ImageSharp/ImageSharp.csproj.DotSettings @@ -1,4 +1,5 @@  + True True True True diff --git a/tests/ImageSharp.Tests/Color/ColorTests.CastFrom.cs b/tests/ImageSharp.Tests/Color/ColorTests.CastFrom.cs new file mode 100644 index 0000000000..22c6d55ad8 --- /dev/null +++ b/tests/ImageSharp.Tests/Color/ColorTests.CastFrom.cs @@ -0,0 +1,93 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests +{ + public partial class ColorTests + { + public class CastFrom + { + [Fact] + public void Rgba64() + { + Rgba64 source = new Rgba64(100, 2222, 3333, 4444); + + // Act: + Color color = source; + + // Assert: + Rgba64 data = color.ToPixel(); + Assert.Equal(source, data); + } + + [Fact] + public void Rgba32() + { + Rgba32 source = new Rgba32(1, 22, 33, 231); + + // Act: + Color color = source; + + // Assert: + Rgba32 data = color.ToPixel(); + Assert.Equal(source, data); + } + + [Fact] + public void Argb32() + { + Argb32 source = new Argb32(1, 22, 33, 231); + + // Act: + Color color = source; + + // Assert: + Argb32 data = color.ToPixel(); + Assert.Equal(source, data); + } + + [Fact] + public void Bgra32() + { + Bgra32 source = new Bgra32(1, 22, 33, 231); + + // Act: + Color color = source; + + // Assert: + Bgra32 data = color.ToPixel(); + Assert.Equal(source, data); + } + + [Fact] + public void Rgb24() + { + Rgb24 source = new Rgb24(1, 22, 231); + + // Act: + Color color = source; + + // Assert: + Rgb24 data = color.ToPixel(); + Assert.Equal(source, data); + } + + [Fact] + public void Bgr24() + { + Bgr24 source = new Bgr24(1, 22, 231); + + // Act: + Color color = source; + + // Assert: + Bgr24 data = color.ToPixel(); + Assert.Equal(source, data); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Color/ColorTests.CastTo.cs b/tests/ImageSharp.Tests/Color/ColorTests.CastTo.cs new file mode 100644 index 0000000000..d44472b28a --- /dev/null +++ b/tests/ImageSharp.Tests/Color/ColorTests.CastTo.cs @@ -0,0 +1,93 @@ +// // Copyright (c) Six Labors and contributors. +// // Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests +{ + public partial class ColorTests + { + public class CastTo + { + [Fact] + public void Rgba64() + { + Rgba64 source = new Rgba64(100, 2222, 3333, 4444); + + // Act: + Color color = new Color(source); + + // Assert: + Rgba64 data = color; + Assert.Equal(source, data); + } + + [Fact] + public void Rgba32() + { + Rgba32 source = new Rgba32(1, 22, 33, 231); + + // Act: + Color color = new Color(source); + + // Assert: + Rgba32 data = color; + Assert.Equal(source, data); + } + + [Fact] + public void Argb32() + { + Argb32 source = new Argb32(1, 22, 33, 231); + + // Act: + Color color = new Color(source); + + // Assert: + Argb32 data = color; + Assert.Equal(source, data); + } + + [Fact] + public void Bgra32() + { + Bgra32 source = new Bgra32(1, 22, 33, 231); + + // Act: + Color color = new Color(source); + + // Assert: + Bgra32 data = color; + Assert.Equal(source, data); + } + + [Fact] + public void Rgb24() + { + Rgb24 source = new Rgb24(1, 22, 231); + + // Act: + Color color = new Color(source); + + // Assert: + Rgb24 data = color; + Assert.Equal(source, data); + } + + [Fact] + public void Bgr24() + { + Bgr24 source = new Bgr24(1, 22, 231); + + // Act: + Color color = new Color(source); + + // Assert: + Bgr24 data = color; + Assert.Equal(source, data); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Color/ColorTests.ConstructFrom.cs b/tests/ImageSharp.Tests/Color/ColorTests.ConstructFrom.cs new file mode 100644 index 0000000000..9d21cd80c5 --- /dev/null +++ b/tests/ImageSharp.Tests/Color/ColorTests.ConstructFrom.cs @@ -0,0 +1,93 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests +{ + public partial class ColorTests + { + public class ConstructFrom + { + [Fact] + public void Rgba64() + { + Rgba64 source = new Rgba64(100, 2222, 3333, 4444); + + // Act: + Color color = new Color(source); + + // Assert: + Rgba64 data = color.ToPixel(); + Assert.Equal(source, data); + } + + [Fact] + public void Rgba32() + { + Rgba32 source = new Rgba32(1, 22, 33, 231); + + // Act: + Color color = new Color(source); + + // Assert: + Rgba32 data = color.ToPixel(); + Assert.Equal(source, data); + } + + [Fact] + public void Argb32() + { + Argb32 source = new Argb32(1, 22, 33, 231); + + // Act: + Color color = new Color(source); + + // Assert: + Argb32 data = color.ToPixel(); + Assert.Equal(source, data); + } + + [Fact] + public void Bgra32() + { + Bgra32 source = new Bgra32(1, 22, 33, 231); + + // Act: + Color color = new Color(source); + + // Assert: + Bgra32 data = color.ToPixel(); + Assert.Equal(source, data); + } + + [Fact] + public void Rgb24() + { + Rgb24 source = new Rgb24(1, 22, 231); + + // Act: + Color color = new Color(source); + + // Assert: + Rgb24 data = color.ToPixel(); + Assert.Equal(source, data); + } + + [Fact] + public void Bgr24() + { + Bgr24 source = new Bgr24(1, 22, 231); + + // Act: + Color color = new Color(source); + + // Assert: + Bgr24 data = color.ToPixel(); + Assert.Equal(source, data); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Color/ColorTests.cs b/tests/ImageSharp.Tests/Color/ColorTests.cs new file mode 100644 index 0000000000..729ef94e3c --- /dev/null +++ b/tests/ImageSharp.Tests/Color/ColorTests.cs @@ -0,0 +1,97 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Linq; +using System.Reflection; + +using SixLabors.ImageSharp.PixelFormats; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests +{ + public partial class ColorTests + { + [Fact] + public void Equality_WhenTrue() + { + Color c1 = new Rgba64(100, 2000, 3000, 40000); + Color c2 = new Rgba64(100, 2000, 3000, 40000); + + Assert.True(c1.Equals(c2)); + Assert.True(c1 == c2); + Assert.False(c1 != c2); + Assert.True(c1.GetHashCode() == c2.GetHashCode()); + } + + [Fact] + public void Equality_WhenFalse() + { + Color c1 = new Rgba64(100, 2000, 3000, 40000); + Color c2 = new Rgba64(101, 2000, 3000, 40000); + Color c3 = new Rgba64(100, 2000, 3000, 40001); + + Assert.False(c1.Equals(c2)); + Assert.False(c2.Equals(c3)); + Assert.False(c3.Equals(c1)); + + Assert.False(c1 == c2); + Assert.True(c1 != c2); + } + + [Fact] + public void ToHex() + { + string expected = "ABCD1234"; + Color color = Color.FromHex(expected); + string actual = color.ToHex(); + + Assert.Equal(expected, actual); + } + + [Fact] + public void WebSafePalette_IsCorrect() + { + Rgba32[] actualPalette = Color.WebSafePalette.ToArray().Select(c => (Rgba32)c).ToArray(); + Assert.Equal(ReferencePalette.WebSafeColors, actualPalette); + } + + [Fact] + public void WernerPalette_IsCorrect() + { + Rgba32[] actualPalette = Color.WernerPalette.ToArray().Select(c => (Rgba32)c).ToArray(); + Assert.Equal(ReferencePalette.WernerColors, actualPalette); + } + + public class FromHex + { + [Fact] + public void ShortHex() + { + Assert.Equal(new Rgb24(255, 255, 255), (Rgb24) Color.FromHex("#fff")); + Assert.Equal(new Rgb24(255, 255, 255), (Rgb24) Color.FromHex("fff")); + Assert.Equal(new Rgba32(0, 0, 0, 255), (Rgba32) Color.FromHex("000f")); + } + + [Fact] + public void LeadingPoundIsOptional() + { + Assert.Equal(new Rgb24(0, 128, 128), (Rgb24) Color.FromHex("#008080")); + Assert.Equal(new Rgb24(0, 128, 128), (Rgb24) Color.FromHex("008080")); + } + + [Fact] + public void ThrowsOnEmpty() + { + Assert.Throws(() => Color.FromHex("")); + } + + [Fact] + public void ThrowsOnNull() + { + Assert.Throws(() => Color.FromHex(null)); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Color/ReferencePalette.cs b/tests/ImageSharp.Tests/Color/ReferencePalette.cs new file mode 100644 index 0000000000..3c6e382c58 --- /dev/null +++ b/tests/ImageSharp.Tests/Color/ReferencePalette.cs @@ -0,0 +1,277 @@ +// // Copyright (c) Six Labors and contributors. +// // Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Tests +{ + internal static class ReferencePalette + { + /// + /// Gets a collection of named, web safe, colors as defined in the CSS Color Module Level 4. + /// + public static readonly Rgba32[] WebSafeColors = + { + Rgba32.AliceBlue, + Rgba32.AntiqueWhite, + Rgba32.Aqua, + Rgba32.Aquamarine, + Rgba32.Azure, + Rgba32.Beige, + Rgba32.Bisque, + Rgba32.Black, + Rgba32.BlanchedAlmond, + Rgba32.Blue, + Rgba32.BlueViolet, + Rgba32.Brown, + Rgba32.BurlyWood, + Rgba32.CadetBlue, + Rgba32.Chartreuse, + Rgba32.Chocolate, + Rgba32.Coral, + Rgba32.CornflowerBlue, + Rgba32.Cornsilk, + Rgba32.Crimson, + Rgba32.Cyan, + Rgba32.DarkBlue, + Rgba32.DarkCyan, + Rgba32.DarkGoldenrod, + Rgba32.DarkGray, + Rgba32.DarkGreen, + Rgba32.DarkKhaki, + Rgba32.DarkMagenta, + Rgba32.DarkOliveGreen, + Rgba32.DarkOrange, + Rgba32.DarkOrchid, + Rgba32.DarkRed, + Rgba32.DarkSalmon, + Rgba32.DarkSeaGreen, + Rgba32.DarkSlateBlue, + Rgba32.DarkSlateGray, + Rgba32.DarkTurquoise, + Rgba32.DarkViolet, + Rgba32.DeepPink, + Rgba32.DeepSkyBlue, + Rgba32.DimGray, + Rgba32.DodgerBlue, + Rgba32.Firebrick, + Rgba32.FloralWhite, + Rgba32.ForestGreen, + Rgba32.Fuchsia, + Rgba32.Gainsboro, + Rgba32.GhostWhite, + Rgba32.Gold, + Rgba32.Goldenrod, + Rgba32.Gray, + Rgba32.Green, + Rgba32.GreenYellow, + Rgba32.Honeydew, + Rgba32.HotPink, + Rgba32.IndianRed, + Rgba32.Indigo, + Rgba32.Ivory, + Rgba32.Khaki, + Rgba32.Lavender, + Rgba32.LavenderBlush, + Rgba32.LawnGreen, + Rgba32.LemonChiffon, + Rgba32.LightBlue, + Rgba32.LightCoral, + Rgba32.LightCyan, + Rgba32.LightGoldenrodYellow, + Rgba32.LightGray, + Rgba32.LightGreen, + Rgba32.LightPink, + Rgba32.LightSalmon, + Rgba32.LightSeaGreen, + Rgba32.LightSkyBlue, + Rgba32.LightSlateGray, + Rgba32.LightSteelBlue, + Rgba32.LightYellow, + Rgba32.Lime, + Rgba32.LimeGreen, + Rgba32.Linen, + Rgba32.Magenta, + Rgba32.Maroon, + Rgba32.MediumAquamarine, + Rgba32.MediumBlue, + Rgba32.MediumOrchid, + Rgba32.MediumPurple, + Rgba32.MediumSeaGreen, + Rgba32.MediumSlateBlue, + Rgba32.MediumSpringGreen, + Rgba32.MediumTurquoise, + Rgba32.MediumVioletRed, + Rgba32.MidnightBlue, + Rgba32.MintCream, + Rgba32.MistyRose, + Rgba32.Moccasin, + Rgba32.NavajoWhite, + Rgba32.Navy, + Rgba32.OldLace, + Rgba32.Olive, + Rgba32.OliveDrab, + Rgba32.Orange, + Rgba32.OrangeRed, + Rgba32.Orchid, + Rgba32.PaleGoldenrod, + Rgba32.PaleGreen, + Rgba32.PaleTurquoise, + Rgba32.PaleVioletRed, + Rgba32.PapayaWhip, + Rgba32.PeachPuff, + Rgba32.Peru, + Rgba32.Pink, + Rgba32.Plum, + Rgba32.PowderBlue, + Rgba32.Purple, + Rgba32.RebeccaPurple, + Rgba32.Red, + Rgba32.RosyBrown, + Rgba32.RoyalBlue, + Rgba32.SaddleBrown, + Rgba32.Salmon, + Rgba32.SandyBrown, + Rgba32.SeaGreen, + Rgba32.SeaShell, + Rgba32.Sienna, + Rgba32.Silver, + Rgba32.SkyBlue, + Rgba32.SlateBlue, + Rgba32.SlateGray, + Rgba32.Snow, + Rgba32.SpringGreen, + Rgba32.SteelBlue, + Rgba32.Tan, + Rgba32.Teal, + Rgba32.Thistle, + Rgba32.Tomato, + Rgba32.Transparent, + Rgba32.Turquoise, + Rgba32.Violet, + Rgba32.Wheat, + Rgba32.White, + Rgba32.WhiteSmoke, + Rgba32.Yellow, + Rgba32.YellowGreen + }; + + /// + /// Gets a collection of colors as defined in the original second edition of Werner’s Nomenclature of Colours 1821. + /// The hex codes were collected and defined by Nicholas Rougeux + /// + public static readonly Rgba32[] WernerColors = + { + Rgba32.FromHex("#f1e9cd"), + Rgba32.FromHex("#f2e7cf"), + Rgba32.FromHex("#ece6d0"), + Rgba32.FromHex("#f2eacc"), + Rgba32.FromHex("#f3e9ca"), + Rgba32.FromHex("#f2ebcd"), + Rgba32.FromHex("#e6e1c9"), + Rgba32.FromHex("#e2ddc6"), + Rgba32.FromHex("#cbc8b7"), + Rgba32.FromHex("#bfbbb0"), + Rgba32.FromHex("#bebeb3"), + Rgba32.FromHex("#b7b5ac"), + Rgba32.FromHex("#bab191"), + Rgba32.FromHex("#9c9d9a"), + Rgba32.FromHex("#8a8d84"), + Rgba32.FromHex("#5b5c61"), + Rgba32.FromHex("#555152"), + Rgba32.FromHex("#413f44"), + Rgba32.FromHex("#454445"), + Rgba32.FromHex("#423937"), + Rgba32.FromHex("#433635"), + Rgba32.FromHex("#252024"), + Rgba32.FromHex("#241f20"), + Rgba32.FromHex("#281f3f"), + Rgba32.FromHex("#1c1949"), + Rgba32.FromHex("#4f638d"), + Rgba32.FromHex("#383867"), + Rgba32.FromHex("#5c6b8f"), + Rgba32.FromHex("#657abb"), + Rgba32.FromHex("#6f88af"), + Rgba32.FromHex("#7994b5"), + Rgba32.FromHex("#6fb5a8"), + Rgba32.FromHex("#719ba2"), + Rgba32.FromHex("#8aa1a6"), + Rgba32.FromHex("#d0d5d3"), + Rgba32.FromHex("#8590ae"), + Rgba32.FromHex("#3a2f52"), + Rgba32.FromHex("#39334a"), + Rgba32.FromHex("#6c6d94"), + Rgba32.FromHex("#584c77"), + Rgba32.FromHex("#533552"), + Rgba32.FromHex("#463759"), + Rgba32.FromHex("#bfbac0"), + Rgba32.FromHex("#77747f"), + Rgba32.FromHex("#4a475c"), + Rgba32.FromHex("#b8bfaf"), + Rgba32.FromHex("#b2b599"), + Rgba32.FromHex("#979c84"), + Rgba32.FromHex("#5d6161"), + Rgba32.FromHex("#61ac86"), + Rgba32.FromHex("#a4b6a7"), + Rgba32.FromHex("#adba98"), + Rgba32.FromHex("#93b778"), + Rgba32.FromHex("#7d8c55"), + Rgba32.FromHex("#33431e"), + Rgba32.FromHex("#7c8635"), + Rgba32.FromHex("#8e9849"), + Rgba32.FromHex("#c2c190"), + Rgba32.FromHex("#67765b"), + Rgba32.FromHex("#ab924b"), + Rgba32.FromHex("#c8c76f"), + Rgba32.FromHex("#ccc050"), + Rgba32.FromHex("#ebdd99"), + Rgba32.FromHex("#ab9649"), + Rgba32.FromHex("#dbc364"), + Rgba32.FromHex("#e6d058"), + Rgba32.FromHex("#ead665"), + Rgba32.FromHex("#d09b2c"), + Rgba32.FromHex("#a36629"), + Rgba32.FromHex("#a77d35"), + Rgba32.FromHex("#f0d696"), + Rgba32.FromHex("#d7c485"), + Rgba32.FromHex("#f1d28c"), + Rgba32.FromHex("#efcc83"), + Rgba32.FromHex("#f3daa7"), + Rgba32.FromHex("#dfa837"), + Rgba32.FromHex("#ebbc71"), + Rgba32.FromHex("#d17c3f"), + Rgba32.FromHex("#92462f"), + Rgba32.FromHex("#be7249"), + Rgba32.FromHex("#bb603c"), + Rgba32.FromHex("#c76b4a"), + Rgba32.FromHex("#a75536"), + Rgba32.FromHex("#b63e36"), + Rgba32.FromHex("#b5493a"), + Rgba32.FromHex("#cd6d57"), + Rgba32.FromHex("#711518"), + Rgba32.FromHex("#e9c49d"), + Rgba32.FromHex("#eedac3"), + Rgba32.FromHex("#eecfbf"), + Rgba32.FromHex("#ce536b"), + Rgba32.FromHex("#b74a70"), + Rgba32.FromHex("#b7757c"), + Rgba32.FromHex("#612741"), + Rgba32.FromHex("#7a4848"), + Rgba32.FromHex("#3f3033"), + Rgba32.FromHex("#8d746f"), + Rgba32.FromHex("#4d3635"), + Rgba32.FromHex("#6e3b31"), + Rgba32.FromHex("#864735"), + Rgba32.FromHex("#553d3a"), + Rgba32.FromHex("#613936"), + Rgba32.FromHex("#7a4b3a"), + Rgba32.FromHex("#946943"), + Rgba32.FromHex("#c39e6d"), + Rgba32.FromHex("#513e32"), + Rgba32.FromHex("#8b7859"), + Rgba32.FromHex("#9b856b"), + Rgba32.FromHex("#766051"), + Rgba32.FromHex("#453b32") + }; + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/ColorTests.cs b/tests/ImageSharp.Tests/ColorTests.cs deleted file mode 100644 index a1eaf9fae3..0000000000 --- a/tests/ImageSharp.Tests/ColorTests.cs +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.PixelFormats; - -using Xunit; - -namespace SixLabors.ImageSharp.Tests -{ - public class ColorTests - { - public class ConstructFrom - { - [Fact] - public void Rgba64() - { - Rgba64 source = new Rgba64(100, 2222, 3333, 4444); - - // Act: - Color color = new Color(source); - - // Assert: - Rgba64 data = color.ToPixel(); - Assert.Equal(source, data); - } - - [Fact] - public void Rgba32() - { - Rgba32 source = new Rgba32(1, 22, 33, 231); - - // Act: - Color color = new Color(source); - - // Assert: - Rgba32 data = color.ToPixel(); - Assert.Equal(source, data); - } - - [Fact] - public void Argb32() - { - Argb32 source = new Argb32(1, 22, 33, 231); - - // Act: - Color color = new Color(source); - - // Assert: - Argb32 data = color.ToPixel(); - Assert.Equal(source, data); - } - - [Fact] - public void Bgra32() - { - Bgra32 source = new Bgra32(1, 22, 33, 231); - - // Act: - Color color = new Color(source); - - // Assert: - Bgra32 data = color.ToPixel(); - Assert.Equal(source, data); - } - - [Fact] - public void Rgb24() - { - Rgb24 source = new Rgb24(1, 22, 231); - - // Act: - Color color = new Color(source); - - // Assert: - Rgb24 data = color.ToPixel(); - Assert.Equal(source, data); - } - - [Fact] - public void Bgr24() - { - Bgr24 source = new Bgr24(1, 22, 231); - - // Act: - Color color = new Color(source); - - // Assert: - Bgr24 data = color.ToPixel(); - Assert.Equal(source, data); - } - } - - public class Cast - { - [Fact] - public void Rgba64() - { - Rgba64 source = new Rgba64(100, 2222, 3333, 4444); - - // Act: - Color color = source; - - // Assert: - Rgba64 data = color.ToPixel(); - Assert.Equal(source, data); - } - - [Fact] - public void Rgba32() - { - Rgba32 source = new Rgba32(1, 22, 33, 231); - - // Act: - Color color = source; - - // Assert: - Rgba32 data = color.ToPixel(); - Assert.Equal(source, data); - } - - [Fact] - public void Argb32() - { - Argb32 source = new Argb32(1, 22, 33, 231); - - // Act: - Color color = source; - - // Assert: - Argb32 data = color.ToPixel(); - Assert.Equal(source, data); - } - - [Fact] - public void Bgra32() - { - Bgra32 source = new Bgra32(1, 22, 33, 231); - - // Act: - Color color = source; - - // Assert: - Bgra32 data = color.ToPixel(); - Assert.Equal(source, data); - } - - [Fact] - public void Rgb24() - { - Rgb24 source = new Rgb24(1, 22, 231); - - // Act: - Color color = source; - - // Assert: - Rgb24 data = color.ToPixel(); - Assert.Equal(source, data); - } - - [Fact] - public void Bgr24() - { - Bgr24 source = new Bgr24(1, 22, 231); - - // Act: - Color color = source; - - // Assert: - Bgr24 data = color.ToPixel(); - Assert.Equal(source, data); - } - } - - public class CastTo - { - [Fact] - public void Rgba64() - { - Rgba64 source = new Rgba64(100, 2222, 3333, 4444); - - // Act: - Color color = new Color(source); - - // Assert: - Rgba64 data = color; - Assert.Equal(source, data); - } - - [Fact] - public void Rgba32() - { - Rgba32 source = new Rgba32(1, 22, 33, 231); - - // Act: - Color color = new Color(source); - - // Assert: - Rgba32 data = color; - Assert.Equal(source, data); - } - - [Fact] - public void Argb32() - { - Argb32 source = new Argb32(1, 22, 33, 231); - - // Act: - Color color = new Color(source); - - // Assert: - Argb32 data = color; - Assert.Equal(source, data); - } - - [Fact] - public void Bgra32() - { - Bgra32 source = new Bgra32(1, 22, 33, 231); - - // Act: - Color color = new Color(source); - - // Assert: - Bgra32 data = color; - Assert.Equal(source, data); - } - - [Fact] - public void Rgb24() - { - Rgb24 source = new Rgb24(1, 22, 231); - - // Act: - Color color = new Color(source); - - // Assert: - Rgb24 data = color; - Assert.Equal(source, data); - } - - [Fact] - public void Bgr24() - { - Bgr24 source = new Bgr24(1, 22, 231); - - // Act: - Color color = new Color(source); - - // Assert: - Bgr24 data = color; - Assert.Equal(source, data); - } - } - } -} \ No newline at end of file From 0426d5ab12f25437c429905a15307484cecf26ae Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 7 May 2019 00:02:54 +0200 Subject: [PATCH 145/223] refactor dithering and error diffusion to use Color --- src/ImageSharp/Color/Color.cs | 67 ++++++++------- .../Processing/DiffuseExtensions.cs | 69 ++++++++-------- src/ImageSharp/Processing/DitherExtensions.cs | 55 ++++++------- .../ErrorDiffusionPaletteProcessor.cs | 77 +++--------------- .../ErrorDiffusionPaletteProcessor{TPixel}.cs | 81 +++++++++++++++++++ .../OrderedDitherPaletteProcessor.cs | 70 +++------------- .../OrderedDitherPaletteProcessor{TPixel}.cs | 79 ++++++++++++++++++ .../Dithering/PaletteDitherProcessor.cs | 37 +++++++++ ...e.cs => PaletteDitherProcessor{TPixel}.cs} | 35 ++++---- .../Processing/Dithering/DitherTest.cs | 42 ++++++---- 10 files changed, 370 insertions(+), 242 deletions(-) create mode 100644 src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor{TPixel}.cs create mode 100644 src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor{TPixel}.cs create mode 100644 src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor.cs rename src/ImageSharp/Processing/Processors/Dithering/{PaletteDitherProcessorBase.cs => PaletteDitherProcessor{TPixel}.cs} (77%) diff --git a/src/ImageSharp/Color/Color.cs b/src/ImageSharp/Color/Color.cs index 3e20cafbd3..bc97393cc1 100644 --- a/src/ImageSharp/Color/Color.cs +++ b/src/ImageSharp/Color/Color.cs @@ -5,6 +5,7 @@ using System; using System.Buffers.Binary; using System.Globalization; using System.Numerics; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.PixelFormats; @@ -117,6 +118,44 @@ namespace SixLabors.ImageSharp /// public override string ToString() => this.ToHex(); + public TPixel ToPixel() + where TPixel : struct, IPixel + { + TPixel pixel = default; + pixel.FromRgba64(this.data); + return pixel; + } + + public bool Equals(Color other) + { + return this.data.PackedValue == other.data.PackedValue; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + { + return false; + } + return obj is Color other && this.Equals(other); + } + + /// + public override int GetHashCode() + { + return this.data.PackedValue.GetHashCode(); + } + + internal static void ToPixel( + Configuration configuration, + ReadOnlySpan source, + Span destination) + where TPixel : struct, IPixel + { + ReadOnlySpan rgba64Span = MemoryMarshal.Cast(source); + PixelOperations.Instance.FromRgba64(Configuration.Default, rgba64Span, destination); + } + /// /// Converts the specified hex value to an rrggbbaa hex value. /// @@ -153,33 +192,5 @@ namespace SixLabors.ImageSharp return new string(new[] { r, r, g, g, b, b, a, a }); } - - public TPixel ToPixel() - where TPixel : struct, IPixel - { - TPixel pixel = default; - pixel.FromRgba64(this.data); - return pixel; - } - - public bool Equals(Color other) - { - return this.data.PackedValue == other.data.PackedValue; - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) - { - return false; - } - return obj is Color other && this.Equals(other); - } - - /// - public override int GetHashCode() - { - return this.data.PackedValue.GetHashCode(); - } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/DiffuseExtensions.cs b/src/ImageSharp/Processing/DiffuseExtensions.cs index 4668363e9a..cd714c3da3 100644 --- a/src/ImageSharp/Processing/DiffuseExtensions.cs +++ b/src/ImageSharp/Processing/DiffuseExtensions.cs @@ -1,14 +1,15 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; +using System; + using SixLabors.ImageSharp.Processing.Processors.Dithering; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Dithering { /// - /// Defines extension methods to apply diffusion to an + /// Defines extension methods to apply diffusion to an /// using Mutate/Clone. /// public static class DiffuseExtensions @@ -16,68 +17,68 @@ namespace SixLabors.ImageSharp.Processing.Dithering /// /// Dithers the image reducing it to a web-safe palette using error diffusion. /// - /// The pixel format. /// The image this method extends. - /// The to allow chaining of operations. - public static IImageProcessingContext Diffuse(this IImageProcessingContext source) - where TPixel : struct, IPixel - => Diffuse(source, KnownDiffusers.FloydSteinberg, .5F); + /// The to allow chaining of operations. + public static IImageProcessingContext Diffuse(this IImageProcessingContext source) => + Diffuse(source, KnownDiffusers.FloydSteinberg, .5F); /// /// Dithers the image reducing it to a web-safe palette using error diffusion. /// - /// The pixel format. /// The image this method extends. /// The threshold to apply binarization of the image. Must be between 0 and 1. - /// The to allow chaining of operations. - public static IImageProcessingContext Diffuse(this IImageProcessingContext source, float threshold) - where TPixel : struct, IPixel - => Diffuse(source, KnownDiffusers.FloydSteinberg, threshold); + /// The to allow chaining of operations. + public static IImageProcessingContext Diffuse(this IImageProcessingContext source, float threshold) => + Diffuse(source, KnownDiffusers.FloydSteinberg, threshold); /// /// Dithers the image reducing it to a web-safe palette using error diffusion. /// - /// The pixel format. /// The image this method extends. /// The diffusion algorithm to apply. /// The threshold to apply binarization of the image. Must be between 0 and 1. - /// The to allow chaining of operations. - public static IImageProcessingContext Diffuse(this IImageProcessingContext source, IErrorDiffuser diffuser, float threshold) - where TPixel : struct, IPixel - => source.ApplyProcessor(new ErrorDiffusionPaletteProcessor(diffuser, threshold)); + /// The to allow chaining of operations. + public static IImageProcessingContext Diffuse( + this IImageProcessingContext source, + IErrorDiffuser diffuser, + float threshold) => + source.ApplyProcessor(new ErrorDiffusionPaletteProcessor(diffuser, threshold)); /// /// Dithers the image reducing it to a web-safe palette using error diffusion. /// - /// The pixel format. /// The image this method extends. /// The diffusion algorithm to apply. /// The threshold to apply binarization of the image. Must be between 0 and 1. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. - public static IImageProcessingContext Diffuse(this IImageProcessingContext source, IErrorDiffuser diffuser, float threshold, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new ErrorDiffusionPaletteProcessor(diffuser, threshold), rectangle); + /// The to allow chaining of operations. + public static IImageProcessingContext Diffuse( + this IImageProcessingContext source, + IErrorDiffuser diffuser, + float threshold, + Rectangle rectangle) => + source.ApplyProcessor(new ErrorDiffusionPaletteProcessor(diffuser, threshold), rectangle); /// /// Dithers the image reducing it to the given palette using error diffusion. /// - /// The pixel format. /// The image this method extends. /// The diffusion algorithm to apply. /// The threshold to apply binarization of the image. Must be between 0 and 1. /// The palette to select substitute colors from. - /// The to allow chaining of operations. - public static IImageProcessingContext Diffuse(this IImageProcessingContext source, IErrorDiffuser diffuser, float threshold, TPixel[] palette) - where TPixel : struct, IPixel - => source.ApplyProcessor(new ErrorDiffusionPaletteProcessor(diffuser, threshold, palette)); + /// The to allow chaining of operations. + public static IImageProcessingContext Diffuse( + this IImageProcessingContext source, + IErrorDiffuser diffuser, + float threshold, + ReadOnlySpan palette) => + source.ApplyProcessor(new ErrorDiffusionPaletteProcessor(diffuser, threshold, palette)); /// /// Dithers the image reducing it to the given palette using error diffusion. /// - /// The pixel format. /// The image this method extends. /// The diffusion algorithm to apply. /// The threshold to apply binarization of the image. Must be between 0 and 1. @@ -85,9 +86,13 @@ namespace SixLabors.ImageSharp.Processing.Dithering /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. - public static IImageProcessingContext Diffuse(this IImageProcessingContext source, IErrorDiffuser diffuser, float threshold, TPixel[] palette, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new ErrorDiffusionPaletteProcessor(diffuser, threshold, palette), rectangle); + /// The to allow chaining of operations. + public static IImageProcessingContext Diffuse( + this IImageProcessingContext source, + IErrorDiffuser diffuser, + float threshold, + ReadOnlySpan palette, + Rectangle rectangle) => + source.ApplyProcessor(new ErrorDiffusionPaletteProcessor(diffuser, threshold, palette), rectangle); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/DitherExtensions.cs b/src/ImageSharp/Processing/DitherExtensions.cs index aeb975d1c1..55794aec20 100644 --- a/src/ImageSharp/Processing/DitherExtensions.cs +++ b/src/ImageSharp/Processing/DitherExtensions.cs @@ -1,14 +1,15 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; +using System; + using SixLabors.ImageSharp.Processing.Processors.Dithering; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Defines dithering extensions to apply on an + /// Defines dithering extensions to apply on an /// using Mutate/Clone. /// public static class DitherExtensions @@ -16,63 +17,63 @@ namespace SixLabors.ImageSharp.Processing /// /// Dithers the image reducing it to a web-safe palette using Bayer4x4 ordered dithering. /// - /// The pixel format. /// The image this method extends. - /// The to allow chaining of operations. - public static IImageProcessingContext Dither(this IImageProcessingContext source) - where TPixel : struct, IPixel - => Dither(source, KnownDitherers.BayerDither4x4); + /// The to allow chaining of operations. + public static IImageProcessingContext Dither(this IImageProcessingContext source) => + Dither(source, KnownDitherers.BayerDither4x4); /// /// Dithers the image reducing it to a web-safe palette using ordered dithering. /// - /// The pixel format. /// The image this method extends. /// The ordered ditherer. - /// The to allow chaining of operations. - public static IImageProcessingContext Dither(this IImageProcessingContext source, IOrderedDither dither) - where TPixel : struct, IPixel - => source.ApplyProcessor(new OrderedDitherPaletteProcessor(dither)); + /// The to allow chaining of operations. + public static IImageProcessingContext Dither(this IImageProcessingContext source, IOrderedDither dither) => + source.ApplyProcessor(new OrderedDitherPaletteProcessor(dither)); /// /// Dithers the image reducing it to the given palette using ordered dithering. /// - /// The pixel format. /// The image this method extends. /// The ordered ditherer. /// The palette to select substitute colors from. - /// The to allow chaining of operations. - public static IImageProcessingContext Dither(this IImageProcessingContext source, IOrderedDither dither, TPixel[] palette) - where TPixel : struct, IPixel - => source.ApplyProcessor(new OrderedDitherPaletteProcessor(dither, palette)); + /// The to allow chaining of operations. + public static IImageProcessingContext Dither( + this IImageProcessingContext source, + IOrderedDither dither, + ReadOnlySpan palette) => + source.ApplyProcessor(new OrderedDitherPaletteProcessor(dither, palette)); /// /// Dithers the image reducing it to a web-safe palette using ordered dithering. /// - /// The pixel format. /// The image this method extends. /// The ordered ditherer. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. - public static IImageProcessingContext Dither(this IImageProcessingContext source, IOrderedDither dither, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new OrderedDitherPaletteProcessor(dither), rectangle); + /// The to allow chaining of operations. + public static IImageProcessingContext Dither( + this IImageProcessingContext source, + IOrderedDither dither, + Rectangle rectangle) => + source.ApplyProcessor(new OrderedDitherPaletteProcessor(dither), rectangle); /// /// Dithers the image reducing it to the given palette using ordered dithering. /// - /// The pixel format. /// The image this method extends. /// The ordered ditherer. /// The palette to select substitute colors from. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. - public static IImageProcessingContext Dither(this IImageProcessingContext source, IOrderedDither dither, TPixel[] palette, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new OrderedDitherPaletteProcessor(dither, palette), rectangle); + /// The to allow chaining of operations. + public static IImageProcessingContext Dither( + this IImageProcessingContext source, + IOrderedDither dither, + ReadOnlySpan palette, + Rectangle rectangle) => + source.ApplyProcessor(new OrderedDitherPaletteProcessor(dither, palette), rectangle); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs index 911d3e8fdc..5436a7733f 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs @@ -2,21 +2,19 @@ // Licensed under the Apache License, Version 2.0. using System; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; + +using SixLabors.ImageSharp.Processing.Processors.Binarization; namespace SixLabors.ImageSharp.Processing.Processors.Dithering { /// - /// An that dithers an image using error diffusion. + /// Defines a dither operation using error diffusion. + /// If no palette is given this will default to the web safe colors defined in the CSS Color Module Level 4. /// - /// The pixel format. - internal class ErrorDiffusionPaletteProcessor : PaletteDitherProcessorBase - where TPixel : struct, IPixel + public sealed class ErrorDiffusionPaletteProcessor : PaletteDitherProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The error diffuser public ErrorDiffusionPaletteProcessor(IErrorDiffuser diffuser) @@ -25,22 +23,22 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The error diffuser /// The threshold to split the image. Must be between 0 and 1. public ErrorDiffusionPaletteProcessor(IErrorDiffuser diffuser, float threshold) - : this(diffuser, threshold, NamedColors.WebSafePalette) + : this(diffuser, threshold, Color.WebSafePalette) { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The error diffuser /// The threshold to split the image. Must be between 0 and 1. /// The palette to select substitute colors from. - public ErrorDiffusionPaletteProcessor(IErrorDiffuser diffuser, float threshold, TPixel[] palette) + public ErrorDiffusionPaletteProcessor(IErrorDiffuser diffuser, float threshold, ReadOnlySpan palette) : base(palette) { Guard.NotNull(diffuser, nameof(diffuser)); @@ -60,59 +58,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// public float Threshold { get; } - /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + /// + public override IImageProcessor CreatePixelSpecificProcessor() { - byte threshold = (byte)MathF.Round(this.Threshold * 255F); - bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8); - - var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); - int startY = interest.Y; - int endY = interest.Bottom; - int startX = interest.X; - int endX = interest.Right; - - // Collect the values before looping so we can reduce our calculation count for identical sibling pixels - TPixel sourcePixel = source[startX, startY]; - TPixel previousPixel = sourcePixel; - PixelPair pair = this.GetClosestPixelPair(ref sourcePixel); - Rgba32 rgba = default; - sourcePixel.ToRgba32(ref rgba); - - // Convert to grayscale using ITU-R Recommendation BT.709 if required - byte luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); - - for (int y = startY; y < endY; y++) - { - Span row = source.GetPixelRowSpan(y); - - for (int x = startX; x < endX; x++) - { - sourcePixel = row[x]; - - // Check if this is the same as the last pixel. If so use that value - // rather than calculating it again. This is an inexpensive optimization. - if (!previousPixel.Equals(sourcePixel)) - { - pair = this.GetClosestPixelPair(ref sourcePixel); - - // No error to spread, exact match. - if (sourcePixel.Equals(pair.First)) - { - continue; - } - - sourcePixel.ToRgba32(ref rgba); - luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); - - // Setup the previous pointer - previousPixel = sourcePixel; - } - - TPixel transformedPixel = luminance >= threshold ? pair.Second : pair.First; - this.Diffuser.Dither(source, sourcePixel, transformedPixel, x, y, startX, startY, endX, endY); - } - } + return new ErrorDiffusionPaletteProcessor(this); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor{TPixel}.cs new file mode 100644 index 0000000000..7edf287e85 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor{TPixel}.cs @@ -0,0 +1,81 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Dithering +{ + /// + /// An that dithers an image using error diffusion. + /// + /// The pixel format. + internal class ErrorDiffusionPaletteProcessor : PaletteDitherProcessor + where TPixel : struct, IPixel + { + public ErrorDiffusionPaletteProcessor(ErrorDiffusionPaletteProcessor definition) + : base(definition) + { + } + + private new ErrorDiffusionPaletteProcessor Definition => (ErrorDiffusionPaletteProcessor)base.Definition; + + /// + protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + byte threshold = (byte)MathF.Round(this.Definition.Threshold * 255F); + bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8); + + var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); + int startY = interest.Y; + int endY = interest.Bottom; + int startX = interest.X; + int endX = interest.Right; + + // Collect the values before looping so we can reduce our calculation count for identical sibling pixels + TPixel sourcePixel = source[startX, startY]; + TPixel previousPixel = sourcePixel; + PixelPair pair = this.GetClosestPixelPair(ref sourcePixel); + Rgba32 rgba = default; + sourcePixel.ToRgba32(ref rgba); + + // Convert to grayscale using ITU-R Recommendation BT.709 if required + byte luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); + + for (int y = startY; y < endY; y++) + { + Span row = source.GetPixelRowSpan(y); + + for (int x = startX; x < endX; x++) + { + sourcePixel = row[x]; + + // Check if this is the same as the last pixel. If so use that value + // rather than calculating it again. This is an inexpensive optimization. + if (!previousPixel.Equals(sourcePixel)) + { + pair = this.GetClosestPixelPair(ref sourcePixel); + + // No error to spread, exact match. + if (sourcePixel.Equals(pair.First)) + { + continue; + } + + sourcePixel.ToRgba32(ref rgba); + luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); + + // Setup the previous pointer + previousPixel = sourcePixel; + } + + TPixel transformedPixel = luminance >= threshold ? pair.Second : pair.First; + this.Definition.Diffuser.Dither(source, sourcePixel, transformedPixel, x, y, startX, startY, endX, endY); + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor.cs b/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor.cs index 1b4910a147..ba7c1e9980 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor.cs @@ -2,35 +2,30 @@ // Licensed under the Apache License, Version 2.0. using System; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Dithering { /// - /// An that dithers an image using error diffusion. + /// Defines a dithering operation that dithers an image using error diffusion. /// If no palette is given this will default to the web safe colors defined in the CSS Color Module Level 4. /// - /// The pixel format. - internal class OrderedDitherPaletteProcessor : PaletteDitherProcessorBase - where TPixel : struct, IPixel + public sealed class OrderedDitherPaletteProcessor : PaletteDitherProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The ordered ditherer. public OrderedDitherPaletteProcessor(IOrderedDither dither) - : this(dither, NamedColors.WebSafePalette) + : this(dither, Color.WebSafePalette) { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The ordered ditherer. /// The palette to select substitute colors from. - public OrderedDitherPaletteProcessor(IOrderedDither dither, TPixel[] palette) + public OrderedDitherPaletteProcessor(IOrderedDither dither, ReadOnlySpan palette) : base(palette) => this.Dither = dither ?? throw new ArgumentNullException(nameof(dither)); /// @@ -38,57 +33,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// public IOrderedDither Dither { get; } - /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + /// + public override IImageProcessor CreatePixelSpecificProcessor() { - bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8); - - var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); - int startY = interest.Y; - int endY = interest.Bottom; - int startX = interest.X; - int endX = interest.Right; - - // Collect the values before looping so we can reduce our calculation count for identical sibling pixels - TPixel sourcePixel = source[startX, startY]; - TPixel previousPixel = sourcePixel; - PixelPair pair = this.GetClosestPixelPair(ref sourcePixel); - Rgba32 rgba = default; - sourcePixel.ToRgba32(ref rgba); - - // Convert to grayscale using ITU-R Recommendation BT.709 if required - byte luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); - - for (int y = startY; y < endY; y++) - { - Span row = source.GetPixelRowSpan(y); - - for (int x = startX; x < endX; x++) - { - sourcePixel = row[x]; - - // Check if this is the same as the last pixel. If so use that value - // rather than calculating it again. This is an inexpensive optimization. - if (!previousPixel.Equals(sourcePixel)) - { - pair = this.GetClosestPixelPair(ref sourcePixel); - - // No error to spread, exact match. - if (sourcePixel.Equals(pair.First)) - { - continue; - } - - sourcePixel.ToRgba32(ref rgba); - luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); - - // Setup the previous pointer - previousPixel = sourcePixel; - } - - this.Dither.Dither(source, sourcePixel, pair.Second, pair.First, luminance, x, y); - } - } + return new OrderedDitherPaletteProcessor(this); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor{TPixel}.cs new file mode 100644 index 0000000000..eefa6e5229 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor{TPixel}.cs @@ -0,0 +1,79 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Dithering +{ + /// + /// An that dithers an image using error diffusion. + /// + /// The pixel format. + internal class OrderedDitherPaletteProcessor : PaletteDitherProcessor + where TPixel : struct, IPixel + { + public OrderedDitherPaletteProcessor(OrderedDitherPaletteProcessor definition) + : base(definition) + { + } + + private new OrderedDitherPaletteProcessor Definition => (OrderedDitherPaletteProcessor)base.Definition; + + /// + protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8); + + var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); + int startY = interest.Y; + int endY = interest.Bottom; + int startX = interest.X; + int endX = interest.Right; + + // Collect the values before looping so we can reduce our calculation count for identical sibling pixels + TPixel sourcePixel = source[startX, startY]; + TPixel previousPixel = sourcePixel; + PixelPair pair = this.GetClosestPixelPair(ref sourcePixel); + Rgba32 rgba = default; + sourcePixel.ToRgba32(ref rgba); + + // Convert to grayscale using ITU-R Recommendation BT.709 if required + byte luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); + + for (int y = startY; y < endY; y++) + { + Span row = source.GetPixelRowSpan(y); + + for (int x = startX; x < endX; x++) + { + sourcePixel = row[x]; + + // Check if this is the same as the last pixel. If so use that value + // rather than calculating it again. This is an inexpensive optimization. + if (!previousPixel.Equals(sourcePixel)) + { + pair = this.GetClosestPixelPair(ref sourcePixel); + + // No error to spread, exact match. + if (sourcePixel.Equals(pair.First)) + { + continue; + } + + sourcePixel.ToRgba32(ref rgba); + luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); + + // Setup the previous pointer + previousPixel = sourcePixel; + } + + this.Definition.Dither.Dither(source, sourcePixel, pair.Second, pair.First, luminance, x, y); + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor.cs b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor.cs new file mode 100644 index 0000000000..904d026345 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor.cs @@ -0,0 +1,37 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors.Dithering +{ + /// + /// The base class for dither and diffusion processors that consume a palette. + /// + public abstract class PaletteDitherProcessor : IImageProcessor + { + private readonly Color[] palette; + + /// + /// Initializes a new instance of the class. + /// + /// The palette to select substitute colors from. + protected PaletteDitherProcessor(ReadOnlySpan palette) + { + // This shouldn't be a perf issue: + // these arrays are small, and created with low frequency. + this.palette = palette.ToArray(); + } + + /// + /// Gets the palette to select substitute colors from. + /// + public ReadOnlySpan Palette => this.palette; + + /// + public abstract IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel; + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessorBase.cs b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs similarity index 77% rename from src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessorBase.cs rename to src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs index 6313b74c94..334eab833c 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessorBase.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs @@ -1,10 +1,11 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; using System.Collections.Generic; using System.Numerics; using System.Runtime.CompilerServices; + using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -14,7 +15,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// The base class for dither and diffusion processors that consume a palette. /// /// The pixel format. - internal abstract class PaletteDitherProcessorBase : ImageProcessor + internal abstract class PaletteDitherProcessor : ImageProcessor where TPixel : struct, IPixel { private readonly Dictionary> cache = new Dictionary>(); @@ -24,19 +25,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// private Vector4[] paletteVector; + private TPixel[] palette; + /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// The palette to select substitute colors from. - protected PaletteDitherProcessorBase(TPixel[] palette) + protected PaletteDitherProcessor(PaletteDitherProcessor definition) { - this.Palette = palette ?? throw new ArgumentNullException(nameof(palette)); + this.Definition = definition; } - /// - /// Gets the palette to select substitute colors from. - /// - public TPixel[] Palette { get; } + protected PaletteDitherProcessor Definition { get; } /// /// Returns the two closest colors from the palette calculated via Euclidean distance in the Rgba space. @@ -74,12 +73,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering { leastDistance = distance; secondClosest = closest; - closest = this.Palette[index]; + closest = this.palette[index]; } else if (distance < secondLeastDistance) { secondLeastDistance = distance; - secondClosest = this.Palette[index]; + secondClosest = this.palette[index]; } } @@ -94,11 +93,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering { base.BeforeFrameApply(source, sourceRectangle, configuration); + // Lazy init palette: + if (this.palette is null) + { + ReadOnlySpan sourcePalette = this.Definition.Palette; + this.palette = new TPixel[sourcePalette.Length]; + Color.ToPixel(configuration, sourcePalette, this.palette); + } + // Lazy init paletteVector: if (this.paletteVector is null) { - this.paletteVector = new Vector4[this.Palette.Length]; - PixelOperations.Instance.ToVector4(configuration, (ReadOnlySpan)this.Palette, (Span)this.paletteVector, PixelConversionModifiers.Scale); + this.paletteVector = new Vector4[this.palette.Length]; + PixelOperations.Instance.ToVector4(configuration, (ReadOnlySpan)this.palette, (Span)this.paletteVector, PixelConversionModifiers.Scale); } } } diff --git a/tests/ImageSharp.Tests/Processing/Dithering/DitherTest.cs b/tests/ImageSharp.Tests/Processing/Dithering/DitherTest.cs index f393d5923d..59dcde2a63 100644 --- a/tests/ImageSharp.Tests/Processing/Dithering/DitherTest.cs +++ b/tests/ImageSharp.Tests/Processing/Dithering/DitherTest.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; + using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Dithering; @@ -12,13 +14,21 @@ namespace SixLabors.ImageSharp.Tests.Processing.Binarization { public class DitherTest : BaseImageOperationsExtensionTest { + private class Assert : Xunit.Assert + { + public static void Equal(ReadOnlySpan a, ReadOnlySpan b) + { + Xunit.Assert.True(a.SequenceEqual(b)); + } + } + private readonly IOrderedDither orderedDither; private readonly IErrorDiffuser errorDiffuser; - private readonly Rgba32[] TestPalette = + private readonly Color[] TestPalette = { - Rgba32.Red, - Rgba32.Green, - Rgba32.Blue + Color.Red, + Color.Green, + Color.Blue }; public DitherTest() @@ -31,24 +41,24 @@ namespace SixLabors.ImageSharp.Tests.Processing.Binarization public void Dither_CorrectProcessor() { this.operations.Dither(this.orderedDither); - OrderedDitherPaletteProcessor p = this.Verify>(); + OrderedDitherPaletteProcessor p = this.Verify(); Assert.Equal(this.orderedDither, p.Dither); - Assert.Equal(NamedColors.WebSafePalette, p.Palette); + Assert.Equal(Color.WebSafePalette.ToArray(), p.Palette); } [Fact] public void Dither_rect_CorrectProcessor() { this.operations.Dither(this.orderedDither, this.rect); - OrderedDitherPaletteProcessor p = this.Verify>(this.rect); + OrderedDitherPaletteProcessor p = this.Verify(this.rect); Assert.Equal(this.orderedDither, p.Dither); - Assert.Equal(NamedColors.WebSafePalette, p.Palette); + Assert.Equal(Color.WebSafePalette, p.Palette); } [Fact] public void Dither_index_CorrectProcessor() { this.operations.Dither(this.orderedDither, this.TestPalette); - OrderedDitherPaletteProcessor p = this.Verify>(); + OrderedDitherPaletteProcessor p = this.Verify(); Assert.Equal(this.orderedDither, p.Dither); Assert.Equal(this.TestPalette, p.Palette); } @@ -57,7 +67,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Binarization public void Dither_index_rect_CorrectProcessor() { this.operations.Dither(this.orderedDither, this.TestPalette, this.rect); - OrderedDitherPaletteProcessor p = this.Verify>(this.rect); + OrderedDitherPaletteProcessor p = this.Verify(this.rect); Assert.Equal(this.orderedDither, p.Dither); Assert.Equal(this.TestPalette, p.Palette); } @@ -67,27 +77,27 @@ namespace SixLabors.ImageSharp.Tests.Processing.Binarization public void Dither_ErrorDiffuser_CorrectProcessor() { this.operations.Diffuse(this.errorDiffuser, .4F); - ErrorDiffusionPaletteProcessor p = this.Verify>(); + ErrorDiffusionPaletteProcessor p = this.Verify(); Assert.Equal(this.errorDiffuser, p.Diffuser); Assert.Equal(.4F, p.Threshold); - Assert.Equal(NamedColors.WebSafePalette, p.Palette); + Assert.Equal(Color.WebSafePalette, p.Palette); } [Fact] public void Dither_ErrorDiffuser_rect_CorrectProcessor() { this.operations.Diffuse(this.errorDiffuser, .3F, this.rect); - ErrorDiffusionPaletteProcessor p = this.Verify>(this.rect); + ErrorDiffusionPaletteProcessor p = this.Verify(this.rect); Assert.Equal(this.errorDiffuser, p.Diffuser); Assert.Equal(.3F, p.Threshold); - Assert.Equal(NamedColors.WebSafePalette, p.Palette); + Assert.Equal(Color.WebSafePalette, p.Palette); } [Fact] public void Dither_ErrorDiffuser_CorrectProcessorWithColors() { this.operations.Diffuse(this.errorDiffuser, .5F, this.TestPalette); - ErrorDiffusionPaletteProcessor p = this.Verify>(); + ErrorDiffusionPaletteProcessor p = this.Verify(); Assert.Equal(this.errorDiffuser, p.Diffuser); Assert.Equal(.5F, p.Threshold); Assert.Equal(this.TestPalette, p.Palette); @@ -97,7 +107,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Binarization public void Dither_ErrorDiffuser_rect_CorrectProcessorWithColors() { this.operations.Diffuse(this.errorDiffuser, .5F, this.TestPalette, this.rect); - ErrorDiffusionPaletteProcessor p = this.Verify>(this.rect); + ErrorDiffusionPaletteProcessor p = this.Verify(this.rect); Assert.Equal(this.errorDiffuser, p.Diffuser); Assert.Equal(.5F, p.Threshold); Assert.Equal(this.TestPalette, p.Palette); From fddae8734a48c5c157ce5682cea22fe2a251b724 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 7 May 2019 19:58:14 +0200 Subject: [PATCH 146/223] Add support for encoding 8-bit bitmaps --- src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs | 7 ++- src/ImageSharp/Formats/Bmp/BmpEncoder.cs | 1 - src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs | 60 ++++++++++++++++++- .../Formats/Bmp/IBmpEncoderOptions.cs | 1 - .../Quantization/QuantizedFrame{TPixel}.cs | 2 +- .../Formats/Bmp/BmpEncoderTests.cs | 9 ++- tests/ImageSharp.Tests/TestImages.cs | 1 + .../TestUtilities/PixelTypes.cs | 2 + tests/Images/Input/Bmp/pal8gs.bmp | 3 + 9 files changed, 79 insertions(+), 7 deletions(-) create mode 100644 tests/Images/Input/Bmp/pal8gs.bmp diff --git a/src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs b/src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs index 38f5c1d662..6e1145beb7 100644 --- a/src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs +++ b/src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs @@ -4,10 +4,15 @@ namespace SixLabors.ImageSharp.Formats.Bmp { /// - /// Enumerates the available bits per pixel for bitmap. + /// Enumerates the available bits per pixel the bitmap encoder supports. /// public enum BmpBitsPerPixel : short { + /// + /// 8 bits per pixel. Each pixel consists of 1 byte. + /// + Pixel8 = 8, + /// /// 16 bits per pixel. Each pixel consists of 2 bytes. /// diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoder.cs b/src/ImageSharp/Formats/Bmp/BmpEncoder.cs index 4efdedb349..f3eb9282ce 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoder.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoder.cs @@ -10,7 +10,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// /// Image encoder for writing an image to a stream as a Windows bitmap. /// - /// The encoder can currently only write 24-bit rgb images to streams. public sealed class BmpEncoder : IImageEncoder, IBmpEncoderOptions { /// diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index 82483e3902..530bcf8cd2 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -9,6 +9,7 @@ using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors.Quantization; using SixLabors.Memory; namespace SixLabors.ImageSharp.Formats.Bmp @@ -43,6 +44,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// private const int Rgba32BlueMask = 0xFF; + /// + /// The color palette for an 8 bit image will have 256 entry's with 4 bytes for each entry. + /// + private const int ColorPaletteSize8Bit = 1024; + private readonly MemoryAllocator memoryAllocator; private Configuration configuration; @@ -142,11 +148,13 @@ namespace SixLabors.ImageSharp.Formats.Bmp infoHeader.Compression = BmpCompression.BitFields; } + int colorPaletteSize = this.bitsPerPixel == BmpBitsPerPixel.Pixel8 ? ColorPaletteSize8Bit : 0; + var fileHeader = new BmpFileHeader( type: BmpConstants.TypeMarkers.Bitmap, fileSize: BmpFileHeader.Size + infoHeaderSize + infoHeader.ImageSize, reserved: 0, - offset: BmpFileHeader.Size + infoHeaderSize); + offset: BmpFileHeader.Size + infoHeaderSize + colorPaletteSize); #if NETCOREAPP2_1 Span buffer = stackalloc byte[infoHeaderSize]; @@ -198,6 +206,10 @@ namespace SixLabors.ImageSharp.Formats.Bmp case BmpBitsPerPixel.Pixel16: this.Write16Bit(stream, pixels); break; + + case BmpBitsPerPixel.Pixel8: + this.Write8Bit(stream, image); + break; } } @@ -276,5 +288,51 @@ namespace SixLabors.ImageSharp.Formats.Bmp } } } + + /// + /// Writes an 8 Bit image with a color palette. The color palette has 256 entry's with 4 bytes for each entry. + /// + /// The type of the pixel. + /// The to write to. + /// The containing pixel data. + private void Write8Bit(Stream stream, ImageFrame image) + where TPixel : struct, IPixel + { +#if NETCOREAPP2_1 + Span colorPalette = stackalloc byte[ColorPaletteSize8Bit]; +#else + byte[] colorPalette = new byte[ColorPaletteSize8Bit]; +#endif + + var quantizer = new WuQuantizer(256); + QuantizedFrame quantized = quantizer.CreateFrameQuantizer(this.configuration).QuantizeFrame(image); + + int idx = 0; + var color = default(Rgba32); + foreach (TPixel quantizedColor in quantized.Palette) + { + quantizedColor.ToRgba32(ref color); + colorPalette[idx] = color.B; + colorPalette[idx + 1] = color.G; + colorPalette[idx + 2] = color.R; + + // Padding byte, always 0 + colorPalette[idx + 3] = 0; + idx += 4; + } + + stream.Write(colorPalette, 0, ColorPaletteSize8Bit); + + for (int y = image.Height - 1; y >= 0; y--) + { + Span pixelSpan = quantized.GetRowSpan(y); + stream.Write(pixelSpan); + + for (int i = 0; i < this.padding; i++) + { + stream.WriteByte(0); + } + } + } } } diff --git a/src/ImageSharp/Formats/Bmp/IBmpEncoderOptions.cs b/src/ImageSharp/Formats/Bmp/IBmpEncoderOptions.cs index 96ec423e7b..92ff092705 100644 --- a/src/ImageSharp/Formats/Bmp/IBmpEncoderOptions.cs +++ b/src/ImageSharp/Formats/Bmp/IBmpEncoderOptions.cs @@ -6,7 +6,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// /// Configuration options for use during bmp encoding /// - /// The encoder can currently only write 16-bit, 24-bit and 32-bit rgb images to streams. internal interface IBmpEncoderOptions { /// diff --git a/src/ImageSharp/Processing/Processors/Quantization/QuantizedFrame{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/QuantizedFrame{TPixel}.cs index 38862ef446..30b8bc8469 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/QuantizedFrame{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/QuantizedFrame{TPixel}.cs @@ -62,7 +62,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// /// Gets the representation of the pixels as a of contiguous memory - /// at row beginning from the the first pixel on that row. + /// at row beginning from the first pixel on that row. /// /// The row. /// The diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs index 7e054734e3..42ec3088d9 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs @@ -114,6 +114,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp [WithFile(WinBmpv3, PixelTypes.Rgb24, BmpBitsPerPixel.Pixel24)] [WithFile(Rgb16, PixelTypes.Bgra5551, BmpBitsPerPixel.Pixel16)] [WithFile(Bit16, PixelTypes.Bgra5551, BmpBitsPerPixel.Pixel16)] + [WithFile(Bit8Palette4, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel8)] + [WithFile(Bit8Gs, PixelTypes.Gray8, BmpBitsPerPixel.Pixel8)] public void Encode_WithV3Header_Works(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) // if supportTransparency is false, a v3 bitmap header will be written where TPixel : struct, IPixel => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: false); @@ -126,11 +128,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp [WithFile(WinBmpv3, PixelTypes.Rgb24, BmpBitsPerPixel.Pixel24)] [WithFile(Rgb16, PixelTypes.Bgra5551, BmpBitsPerPixel.Pixel16)] [WithFile(Bit16, PixelTypes.Bgra5551, BmpBitsPerPixel.Pixel16)] + [WithFile(Bit8Palette4, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel8)] + [WithFile(Bit8Gs, PixelTypes.Gray8, BmpBitsPerPixel.Pixel8)] public void Encode_WithV4Header_Works(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) where TPixel : struct, IPixel => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: true); [Theory] [WithFile(TestImages.Png.GrayAlpha2BitInterlaced, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel32)] + [WithFile(Bit32Rgba, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel32)] public void Encode_PreservesAlpha(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) where TPixel : struct, IPixel => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: true); @@ -139,8 +144,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp { using (Image image = provider.GetImage()) { - // There is no alpha in bmp with 24 bits per pixels, so the reference image will be made opaque. - if (bitsPerPixel == BmpBitsPerPixel.Pixel24) + // There is no alpha in bmp with less then 32 bits per pixels, so the reference image will be made opaque. + if (bitsPerPixel != BmpBitsPerPixel.Pixel32) { image.Mutate(c => c.MakeOpaque()); } diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index f82278ef5d..62b7ae2ec0 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -236,6 +236,7 @@ namespace SixLabors.ImageSharp.Tests public const string Bit1Pal1 = "Bmp/pal1p1.bmp"; public const string Bit4 = "Bmp/pal4.bmp"; public const string Bit8 = "Bmp/test8.bmp"; + public const string Bit8Gs = "Bmp/pal8gs.bmp"; public const string Bit8Inverted = "Bmp/test8-inverted.bmp"; public const string Bit16 = "Bmp/test16.bmp"; public const string Bit16Inverted = "Bmp/test16-inverted.bmp"; diff --git a/tests/ImageSharp.Tests/TestUtilities/PixelTypes.cs b/tests/ImageSharp.Tests/TestUtilities/PixelTypes.cs index e4a7572d60..78431f31aa 100644 --- a/tests/ImageSharp.Tests/TestUtilities/PixelTypes.cs +++ b/tests/ImageSharp.Tests/TestUtilities/PixelTypes.cs @@ -60,6 +60,8 @@ namespace SixLabors.ImageSharp.Tests Bgra5551 = 1 << 22, + Gray8 = 1 << 23, + // TODO: Add multi-flag entries by rules defined in PackedPixelConverterHelper // "All" is handled as a separate, individual case instead of using bitwise OR diff --git a/tests/Images/Input/Bmp/pal8gs.bmp b/tests/Images/Input/Bmp/pal8gs.bmp new file mode 100644 index 0000000000..359499c7a5 --- /dev/null +++ b/tests/Images/Input/Bmp/pal8gs.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:abb09008dc6af0b33db70ed01e4183f946cc90b647bd84b078794b2d97eb9c33 +size 9254 From fc9c716ece921fb76c1eebc13ab875aa1ce441cf Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 7 May 2019 20:43:12 +0200 Subject: [PATCH 147/223] Changed WuQuantizer to OctreeQuantizer --- src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index 530bcf8cd2..0ef2046a84 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -304,7 +304,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp byte[] colorPalette = new byte[ColorPaletteSize8Bit]; #endif - var quantizer = new WuQuantizer(256); + var quantizer = new OctreeQuantizer(256); QuantizedFrame quantized = quantizer.CreateFrameQuantizer(this.configuration).QuantizeFrame(image); int idx = 0; From b902246a29ce163256b8b37e436e96ac387f65e9 Mon Sep 17 00:00:00 2001 From: SLane35 Date: Thu, 9 May 2019 02:46:40 +0300 Subject: [PATCH 148/223] Update Readme (#905) A few minor grammatical corrections. --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index ceb28564b9..515fc71969 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Designed to democratize image processing, ImageSharp brings you an incredibly po Compared to `System.Drawing` we have been able to develop something much more flexible, easier to code against, and much, much less prone to memory leaks. Gone are system-wide process-locks; ImageSharp images are thread-safe and fully supported in web environments. -Built against .NET Standard 1.3 ImageSharp can be used in device, cloud, and embedded/IoT scenarios. +Built against .NET Standard 1.3, ImageSharp can be used in device, cloud, and embedded/IoT scenarios. ### Documentation For all SixLabors projects, including ImageSharp: @@ -41,15 +41,15 @@ Install stable releases via Nuget; development releases are available via MyGet. The **ImageSharp** library is made up of multiple packages: - **SixLabors.ImageSharp** - - Contains the generic `Image` class, PixelFormats, Primitives, Configuration, and other core functionality. - - The `IImageFormat` interface, Jpeg, Png, Bmp, and Gif formats. - - Transform methods like Resize, Crop, Skew, Rotate - Anything that alters the dimensions of the image. - - Non-transform methods like Gaussian Blur, Pixelate, Edge Detection - Anything that maintains the original image dimensions. + - Contains the generic `Image` class, PixelFormats, Primitives, Configuration, and other core functionality + - The `IImageFormat` interface, Jpeg, Png, Bmp, and Gif formats + - Transform methods like Resize, Crop, Skew, Rotate - anything that alters the dimensions of the image + - Non-transform methods like Gaussian Blur, Pixelate, Edge Detection - anything that maintains the original image dimensions - **SixLabors.ImageSharp.Drawing** - - Brushes and various drawing algorithms, including drawing images. + - Brushes and various drawing algorithms, including drawing images - Various vector drawing methods for drawing paths, polygons etc. - - Text drawing. + - Text drawing ### Build Status @@ -117,7 +117,7 @@ Alternatively, you can work from command line and/or with a lightweight editor o - [Visual Studio Code](https://code.visualstudio.com/) with [C# Extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.csharp) - [.NET Core](https://www.microsoft.com/net/core#linuxubuntu) -To clone ImageSharp locally click the "Clone in Windows" button above or run the following git commands. +To clone ImageSharp locally, click the "Clone in Windows" button above or run the following git commands: ```bash git clone https://github.com/SixLabors/ImageSharp From 75d5bd812e917398ae90e16917ac6fcf154e0ee0 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Thu, 9 May 2019 20:35:19 +0200 Subject: [PATCH 149/223] Trying MagickReferenceDecoder for the 8bit greyscale image encoder tests --- .../Formats/Bmp/BmpEncoderTests.cs | 50 +++++++++++++++---- 1 file changed, 39 insertions(+), 11 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs index 42ec3088d9..a56cee47d8 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs @@ -2,10 +2,13 @@ // Licensed under the Apache License, Version 2.0. using System.IO; + +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; using Xunit; using Xunit.Abstractions; @@ -110,13 +113,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp [WithFile(Bit32Rgba, PixelTypes.Rgba32 | PixelTypes.Rgb24, BmpBitsPerPixel.Pixel32)] [WithFile(WinBmpv4, PixelTypes.Rgba32 | PixelTypes.Rgb24, BmpBitsPerPixel.Pixel32)] [WithFile(WinBmpv5, PixelTypes.Rgba32 | PixelTypes.Rgb24, BmpBitsPerPixel.Pixel32)] - // WinBmpv3 is a 24 bits per pixel image - [WithFile(WinBmpv3, PixelTypes.Rgb24, BmpBitsPerPixel.Pixel24)] - [WithFile(Rgb16, PixelTypes.Bgra5551, BmpBitsPerPixel.Pixel16)] - [WithFile(Bit16, PixelTypes.Bgra5551, BmpBitsPerPixel.Pixel16)] - [WithFile(Bit8Palette4, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel8)] - [WithFile(Bit8Gs, PixelTypes.Gray8, BmpBitsPerPixel.Pixel8)] - public void Encode_WithV3Header_Works(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) + public void Encode_32Bit_WithV3Header_Works(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) // if supportTransparency is false, a v3 bitmap header will be written where TPixel : struct, IPixel => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: false); @@ -125,13 +122,44 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp [WithFile(Bit32Rgba, PixelTypes.Rgba32 | PixelTypes.Rgb24, BmpBitsPerPixel.Pixel32)] [WithFile(WinBmpv4, PixelTypes.Rgba32 | PixelTypes.Rgb24, BmpBitsPerPixel.Pixel32)] [WithFile(WinBmpv5, PixelTypes.Rgba32 | PixelTypes.Rgb24, BmpBitsPerPixel.Pixel32)] + public void Encode_32Bit_WithV4Header_Works(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) + where TPixel : struct, IPixel => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: true); + + [Theory] + // WinBmpv3 is a 24 bits per pixel image + [WithFile(WinBmpv3, PixelTypes.Rgb24, BmpBitsPerPixel.Pixel24)] + public void Encode_24Bit_WithV3Header_Works(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) + where TPixel : struct, IPixel => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: false); + + [Theory] [WithFile(WinBmpv3, PixelTypes.Rgb24, BmpBitsPerPixel.Pixel24)] + public void Encode_24Bit_WithV4Header_Works(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) + where TPixel : struct, IPixel => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: true); + + + [Theory] + [WithFile(Rgb16, PixelTypes.Bgra5551, BmpBitsPerPixel.Pixel16)] + [WithFile(Bit16, PixelTypes.Bgra5551, BmpBitsPerPixel.Pixel16)] + public void Encode_16Bit_WithV3Header_Works(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) + where TPixel : struct, IPixel => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: false); + + [Theory] [WithFile(Rgb16, PixelTypes.Bgra5551, BmpBitsPerPixel.Pixel16)] [WithFile(Bit16, PixelTypes.Bgra5551, BmpBitsPerPixel.Pixel16)] + public void Encode_16Bit_WithV4Header_Works(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) + where TPixel : struct, IPixel => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: true); + + [Theory] [WithFile(Bit8Palette4, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel8)] [WithFile(Bit8Gs, PixelTypes.Gray8, BmpBitsPerPixel.Pixel8)] - public void Encode_WithV4Header_Works(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) - where TPixel : struct, IPixel => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: true); + public void Encode_8Bit_WithV3Header_Works(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) + where TPixel : struct, IPixel => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: false, referenceDecoder: new MagickReferenceDecoder()); + + [Theory] + [WithFile(Bit8Palette4, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel8)] + [WithFile(Bit8Gs, PixelTypes.Gray8, BmpBitsPerPixel.Pixel8)] + public void Encode_8Bit_WithV4Header_Works(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) + where TPixel : struct, IPixel => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: true, referenceDecoder: new MagickReferenceDecoder()); [Theory] [WithFile(TestImages.Png.GrayAlpha2BitInterlaced, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel32)] @@ -139,7 +167,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp public void Encode_PreservesAlpha(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) where TPixel : struct, IPixel => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: true); - private static void TestBmpEncoderCore(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel, bool supportTransparency = true) + private static void TestBmpEncoderCore(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel, bool supportTransparency = true, IImageDecoder referenceDecoder = null) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) @@ -153,7 +181,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp var encoder = new BmpEncoder { BitsPerPixel = bitsPerPixel, SupportTransparency = supportTransparency }; // Does DebugSave & load reference CompareToReferenceInput(): - image.VerifyEncoder(provider, "bmp", bitsPerPixel, encoder); + image.VerifyEncoder(provider, "bmp", bitsPerPixel, encoder, referenceDecoder: referenceDecoder); } } } From 1140910d84c4bd23df5fb91507717d35139b69ed Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Thu, 9 May 2019 22:07:04 +0200 Subject: [PATCH 150/223] Setting dither to false in the OctreeQuantizer --- src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs | 2 +- .../Processors/Quantization/OctreeQuantizer.cs | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index 0ef2046a84..be82e2cab3 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -304,7 +304,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp byte[] colorPalette = new byte[ColorPaletteSize8Bit]; #endif - var quantizer = new OctreeQuantizer(256); + var quantizer = new OctreeQuantizer(dither: false, maxColors: 256); QuantizedFrame quantized = quantizer.CreateFrameQuantizer(this.configuration).QuantizeFrame(image); int idx = 0; diff --git a/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer.cs b/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer.cs index d49023886b..f5fa8c95d9 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer.cs @@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// /// Initializes a new instance of the class. /// - /// Whether to apply dithering to the output image + /// Whether to apply dithering to the output image. public OctreeQuantizer(bool dither) : this(GetDiffuser(dither), QuantizerConstants.MaxColors) { @@ -44,7 +44,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// /// Initializes a new instance of the class. /// - /// The error diffusion algorithm, if any, to apply to the output image + /// The maximum number of colors to hold in the color palette. + /// Whether to apply dithering to the output image. + public OctreeQuantizer(bool dither, int maxColors) + : this(GetDiffuser(dither), maxColors) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The error diffusion algorithm, if any, to apply to the output image. public OctreeQuantizer(IErrorDiffuser diffuser) : this(diffuser, QuantizerConstants.MaxColors) { @@ -53,8 +63,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// /// Initializes a new instance of the class. /// - /// The error diffusion algorithm, if any, to apply to the output image - /// The maximum number of colors to hold in the color palette + /// The error diffusion algorithm, if any, to apply to the output image. + /// The maximum number of colors to hold in the color palette. public OctreeQuantizer(IErrorDiffuser diffuser, int maxColors) { this.Diffuser = diffuser; From a7720e53cbe9570fc56e251a44aa96b574a8d34c Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 9 May 2019 22:18:18 +0200 Subject: [PATCH 151/223] verbose naming for Histogram Equalization stuff + make it public --- ... AdaptiveHistogramEqualizationProcessor.cs} | 18 +++++++++++++----- ...eHistogramEqualizationProcessor{TPixel}.cs} | 6 +++--- ...ogramEqualizationSlidingWindowProcessor.cs} | 16 ++++++++++++---- ...alizationSlidingWindowProcessor{TPixel}.cs} | 6 +++--- .../GlobalHistogramEqualizationProcessor.cs | 8 +++++++- .../HistogramEqualizationProcessor.cs | 6 +++--- 6 files changed, 41 insertions(+), 19 deletions(-) rename src/ImageSharp/Processing/Processors/Normalization/{AdaptiveHistEqualizationProcessor.cs => AdaptiveHistogramEqualizationProcessor.cs} (76%) rename src/ImageSharp/Processing/Processors/Normalization/{AdaptiveHistEqualizationProcessor{TPixel}.cs => AdaptiveHistogramEqualizationProcessor{TPixel}.cs} (98%) rename src/ImageSharp/Processing/Processors/Normalization/{AdaptiveHistEqualizationSWProcessor.cs => AdaptiveHistogramEqualizationSlidingWindowProcessor.cs} (72%) rename src/ImageSharp/Processing/Processors/Normalization/{AdaptiveHistEqualizationSWProcessor{TPixel}.cs => AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs} (98%) diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor.cs similarity index 76% rename from src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs rename to src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor.cs index 4fd0f853d0..ad27ae020c 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor.cs @@ -7,17 +7,21 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// Applies an adaptive histogram equalization to the image. The image is split up in tiles. For each tile a cumulative distribution function (cdf) is calculated. /// To calculate the final equalized pixel value, the cdf value of four adjacent tiles will be interpolated. /// - internal class AdaptiveHistEqualizationProcessor : HistogramEqualizationProcessor + public class AdaptiveHistogramEqualizationProcessor : HistogramEqualizationProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images /// or 65536 for 16-bit grayscale images. /// Indicating whether to clip the histogram bins at a specific value. /// Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. /// The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100. - public AdaptiveHistEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage, int numberOfTiles) + public AdaptiveHistogramEqualizationProcessor( + int luminanceLevels, + bool clipHistogram, + float clipLimitPercentage, + int numberOfTiles) : base(luminanceLevels, clipHistogram, clipLimitPercentage) { this.NumberOfTiles = numberOfTiles; @@ -31,7 +35,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// public override IImageProcessor CreatePixelSpecificProcessor() { - return new AdaptiveHistEqualizationProcessor(this.LuminanceLevels, this.ClipHistogram, this.ClipLimitPercentage, this.NumberOfTiles); + return new AdaptiveHistogramEqualizationProcessor( + this.LuminanceLevels, + this.ClipHistogram, + this.ClipLimitPercentage, + this.NumberOfTiles); } } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs similarity index 98% rename from src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor{TPixel}.cs rename to src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs index 930c4010fa..333444eb39 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs @@ -21,18 +21,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// To calculate the final equalized pixel value, the cdf value of four adjacent tiles will be interpolated. /// /// The pixel format. - internal class AdaptiveHistEqualizationProcessor : HistogramEqualizationProcessor + internal class AdaptiveHistogramEqualizationProcessor : HistogramEqualizationProcessor where TPixel : struct, IPixel { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images /// or 65536 for 16-bit grayscale images. /// Indicating whether to clip the histogram bins at a specific value. /// Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. /// The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100. - public AdaptiveHistEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage, int tiles) + public AdaptiveHistogramEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage, int tiles) : base(luminanceLevels, clipHistogram, clipLimitPercentage) { Guard.MustBeGreaterThanOrEqualTo(tiles, 2, nameof(tiles)); diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor.cs similarity index 72% rename from src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor.cs rename to src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor.cs index cd4a9644ff..36f798975a 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor.cs @@ -6,17 +6,21 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// /// Applies an adaptive histogram equalization to the image using an sliding window approach. /// - internal class AdaptiveHistEqualizationSWProcessor : HistogramEqualizationProcessor + public class AdaptiveHistogramEqualizationSlidingWindowProcessor : HistogramEqualizationProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images /// or 65536 for 16-bit grayscale images. /// Indicating whether to clip the histogram bins at a specific value. /// Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. /// The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100. - public AdaptiveHistEqualizationSWProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage, int numberOfTiles) + public AdaptiveHistogramEqualizationSlidingWindowProcessor( + int luminanceLevels, + bool clipHistogram, + float clipLimitPercentage, + int numberOfTiles) : base(luminanceLevels, clipHistogram, clipLimitPercentage) { this.NumberOfTiles = numberOfTiles; @@ -30,7 +34,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// public override IImageProcessor CreatePixelSpecificProcessor() { - return new AdaptiveHistEqualizationSWProcessor(this.LuminanceLevels, this.ClipHistogram, this.ClipLimitPercentage, this.NumberOfTiles); + return new AdaptiveHistogramEqualizationSlidingWindowProcessor( + this.LuminanceLevels, + this.ClipHistogram, + this.ClipLimitPercentage, + this.NumberOfTiles); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs similarity index 98% rename from src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor{TPixel}.cs rename to src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs index 3584b1a88b..40e2d41d9b 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs @@ -20,18 +20,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// Applies an adaptive histogram equalization to the image using an sliding window approach. /// /// The pixel format. - internal class AdaptiveHistEqualizationSWProcessor : HistogramEqualizationProcessor + internal class AdaptiveHistogramEqualizationSlidingWindowProcessor : HistogramEqualizationProcessor where TPixel : struct, IPixel { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images /// or 65536 for 16-bit grayscale images. /// Indicating whether to clip the histogram bins at a specific value. /// Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. /// The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100. - public AdaptiveHistEqualizationSWProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage, int tiles) + public AdaptiveHistogramEqualizationSlidingWindowProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage, int tiles) : base(luminanceLevels, clipHistogram, clipLimitPercentage) { Guard.MustBeGreaterThanOrEqualTo(tiles, 2, nameof(tiles)); diff --git a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs index 62e0185950..9af2c8352b 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs @@ -6,8 +6,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// /// Defines a global histogram equalization applicable to an . /// - internal class GlobalHistogramEqualizationProcessor : HistogramEqualizationProcessor + public class GlobalHistogramEqualizationProcessor : HistogramEqualizationProcessor { + /// + /// Initializes a new instance of the class. + /// + /// The number of luminance levels. + /// A value indicating whether to clip the histogram bins at a specific value. + /// The histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. public GlobalHistogramEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage) : base(luminanceLevels, clipHistogram, clipLimitPercentage) { diff --git a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs index 4aad1f564e..b1d12f8478 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// /// Defines a processor that normalizes the histogram of an image. /// - internal abstract class HistogramEqualizationProcessor : IImageProcessor + public abstract class HistogramEqualizationProcessor : IImageProcessor { /// /// Initializes a new instance of the class. @@ -63,7 +63,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization break; case HistogramEqualizationMethod.AdaptiveTileInterpolation: - processor = new AdaptiveHistEqualizationProcessor( + processor = new AdaptiveHistogramEqualizationProcessor( options.LuminanceLevels, options.ClipHistogram, options.ClipLimitPercentage, @@ -71,7 +71,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization break; case HistogramEqualizationMethod.AdaptiveSlidingWindow: - processor = new AdaptiveHistEqualizationSWProcessor( + processor = new AdaptiveHistogramEqualizationSlidingWindowProcessor( options.LuminanceLevels, options.ClipHistogram, options.ClipLimitPercentage, From b92731896daab0ff627c7a8e65b07a4ec8f3bffb Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 9 May 2019 22:32:27 +0200 Subject: [PATCH 152/223] formatting --- .../AffineTransformProcessor{TPixel}.cs | 21 ++++-- .../Transforms/AutoOrientProcessor{TPixel}.cs | 5 +- .../ProjectiveTransformProcessor{TPixel}.cs | 27 ++++++-- .../Transforms/RotateProcessor{TPixel}.cs | 68 +++++++++++-------- 4 files changed, 80 insertions(+), 41 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs index e57ce826bd..5a043cb207 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs @@ -25,18 +25,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms this.Definition = definition; } + protected AffineTransformProcessor Definition { get; } + private Size TargetDimensions => this.Definition.TargetDimensions; private Matrix3x2 TransformMatrix => this.Definition.TransformMatrix; - protected AffineTransformProcessor Definition { get; } - /// protected override Image CreateDestination(Image source, Rectangle sourceRectangle) { // We will always be creating the clone even for mutate because we may need to resize the canvas - IEnumerable> frames = - source.Frames.Select(x => new ImageFrame(source.GetConfiguration(), this.TargetDimensions, x.Metadata.DeepClone())); + IEnumerable> frames = source.Frames.Select( + x => new ImageFrame(source.GetConfiguration(), this.TargetDimensions, x.Metadata.DeepClone())); // Use the overload to prevent an extra frame being added return new Image(source.GetConfiguration(), source.Metadata.DeepClone(), frames); @@ -111,10 +111,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms // Use the single precision position to calculate correct bounding pixels // otherwise we get rogue pixels outside of the bounds. var point = Vector2.Transform(new Vector2(x, y), matrix); - kernel.Convolve(point, x, ref ySpanRef, ref xSpanRef, source.PixelBuffer, vectorSpan); + kernel.Convolve( + point, + x, + ref ySpanRef, + ref xSpanRef, + source.PixelBuffer, + vectorSpan); } - PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan, targetRowSpan); + PixelOperations.Instance.FromVector4Destructive( + configuration, + vectorSpan, + targetRowSpan); } }); } diff --git a/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs index 257c223dc4..8b3ec8690e 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs @@ -61,7 +61,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } /// - protected override void OnFrameApply(ImageFrame sourceBase, Rectangle sourceRectangle, Configuration config) + protected override void OnFrameApply( + ImageFrame sourceBase, + Rectangle sourceRectangle, + Configuration config) { // All processing happens at the image level within BeforeImageApply(); } diff --git a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs index e6d885803b..ab07040f71 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs @@ -33,15 +33,23 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms protected override Image CreateDestination(Image source, Rectangle sourceRectangle) { // We will always be creating the clone even for mutate because we may need to resize the canvas - IEnumerable> frames = - source.Frames.Select(x => new ImageFrame(source.GetConfiguration(), this.TargetDimensions.Width, this.TargetDimensions.Height, x.Metadata.DeepClone())); + IEnumerable> frames = source.Frames.Select( + x => new ImageFrame( + source.GetConfiguration(), + this.TargetDimensions.Width, + this.TargetDimensions.Height, + x.Metadata.DeepClone())); // Use the overload to prevent an extra frame being added return new Image(source.GetConfiguration(), source.Metadata.DeepClone(), frames); } /// - protected override void OnFrameApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) + protected override void OnFrameApply( + ImageFrame source, + ImageFrame destination, + Rectangle sourceRectangle, + Configuration configuration) { Matrix4x4 transformMatrix = this.definition.TransformMatrix; @@ -110,10 +118,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms // Use the single precision position to calculate correct bounding pixels // otherwise we get rogue pixels outside of the bounds. Vector2 point = TransformUtils.ProjectiveTransform2D(x, y, matrix); - kernel.Convolve(point, x, ref ySpanRef, ref xSpanRef, source.PixelBuffer, vectorSpan); + kernel.Convolve( + point, + x, + ref ySpanRef, + ref xSpanRef, + source.PixelBuffer, + vectorSpan); } - PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan, targetRowSpan); + PixelOperations.Instance.FromVector4Destructive( + configuration, + vectorSpan, + targetRowSpan); } }); } diff --git a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs index aac6f65148..252cb77aba 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs @@ -27,18 +27,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms private float Degrees { get; } /// - protected override void OnFrameApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) - { - if (this.OptimizedApply(source, destination, configuration)) - { - return; - } - - base.OnFrameApply(source, destination, sourceRectangle, configuration); - } - - /// - protected override void AfterImageApply(Image source, Image destination, Rectangle sourceRectangle) + protected override void AfterImageApply( + Image source, + Image destination, + Rectangle sourceRectangle) { ExifProfile profile = destination.Metadata.ExifProfile; if (profile is null) @@ -57,6 +49,21 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms base.AfterImageApply(source, destination, sourceRectangle); } + /// + protected override void OnFrameApply( + ImageFrame source, + ImageFrame destination, + Rectangle sourceRectangle, + Configuration configuration) + { + if (this.OptimizedApply(source, destination, configuration)) + { + return; + } + + base.OnFrameApply(source, destination, sourceRectangle, configuration); + } + /// /// Wraps a given angle in degrees so that it falls withing the 0-360 degree range /// @@ -83,7 +90,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// /// The /// - private bool OptimizedApply(ImageFrame source, ImageFrame destination, Configuration configuration) + private bool OptimizedApply( + ImageFrame source, + ImageFrame destination, + Configuration configuration) { // Wrap the degrees to keep within 0-360 so we can apply optimizations when possible. float degrees = WrapDegrees(this.Degrees); @@ -117,16 +127,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } /// - /// Rotates the image 270 degrees clockwise at the centre point. + /// Rotates the image 180 degrees clockwise at the centre point. /// /// The source image. /// The destination image. /// The configuration. - private void Rotate270(ImageFrame source, ImageFrame destination, Configuration configuration) + private void Rotate180(ImageFrame source, ImageFrame destination, Configuration configuration) { int width = source.Width; int height = source.Height; - Rectangle destinationBounds = destination.Bounds(); ParallelHelper.IterateRows( source.Bounds(), @@ -136,31 +145,27 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms for (int y = rows.Min; y < rows.Max; y++) { Span sourceRow = source.GetPixelRowSpan(y); + Span targetRow = destination.GetPixelRowSpan(height - y - 1); + for (int x = 0; x < width; x++) { - int newX = height - y - 1; - newX = height - newX - 1; - int newY = width - x - 1; - - if (destinationBounds.Contains(newX, newY)) - { - destination[newX, newY] = sourceRow[x]; - } + targetRow[width - x - 1] = sourceRow[x]; } } }); } /// - /// Rotates the image 180 degrees clockwise at the centre point. + /// Rotates the image 270 degrees clockwise at the centre point. /// /// The source image. /// The destination image. /// The configuration. - private void Rotate180(ImageFrame source, ImageFrame destination, Configuration configuration) + private void Rotate270(ImageFrame source, ImageFrame destination, Configuration configuration) { int width = source.Width; int height = source.Height; + Rectangle destinationBounds = destination.Bounds(); ParallelHelper.IterateRows( source.Bounds(), @@ -170,11 +175,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms for (int y = rows.Min; y < rows.Max; y++) { Span sourceRow = source.GetPixelRowSpan(y); - Span targetRow = destination.GetPixelRowSpan(height - y - 1); - for (int x = 0; x < width; x++) { - targetRow[width - x - 1] = sourceRow[x]; + int newX = height - y - 1; + newX = height - newX - 1; + int newY = width - x - 1; + + if (destinationBounds.Contains(newX, newY)) + { + destination[newX, newY] = sourceRow[x]; + } } } }); From 2168ecbb05c36eff9af4f134d271099caf3f2ee4 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 9 May 2019 23:39:05 +0200 Subject: [PATCH 153/223] refactor of Overlays --- src/ImageSharp/Color/Color.cs | 2 + src/ImageSharp/Primitives/ValueSize.cs | 2 +- .../Processing/BackgroundColorExtensions.cs | 45 ++++--- src/ImageSharp/Processing/GlowExtensions.cs | 98 +++++++------- .../Filters/LomographProcessor{TPixel}.cs | 4 +- .../Filters/PolaroidProcessor{TPixel}.cs | 14 +- .../Overlays/BackgroundColorProcessor.cs | 86 ++---------- .../BackgroundColorProcessor{TPixel}.cs | 101 ++++++++++++++ .../Processors/Overlays/GlowProcessor.cs | 125 ++++-------------- .../Overlays/GlowProcessor{TPixel}.cs | 112 ++++++++++++++++ .../Processors/Overlays/VignetteProcessor.cs | 120 +++-------------- .../Overlays/VignetteProcessor{TPixel}.cs | 114 ++++++++++++++++ .../Processing/VignetteExtensions.cs | 121 ++++++++++------- tests/ImageSharp.Benchmarks/Samplers/Glow.cs | 4 +- .../ImageSharp.Tests/Drawing/BeziersTests.cs | 6 +- .../ImageSharp.Tests/Drawing/DrawImageTest.cs | 2 +- .../ImageSharp.Tests/Drawing/DrawPathTests.cs | 8 +- .../Drawing/LineComplexPolygonTests.cs | 17 ++- tests/ImageSharp.Tests/Drawing/LineTests.cs | 33 ++--- .../ImageSharp.Tests/Drawing/PolygonTests.cs | 9 +- .../Drawing/SolidBezierTests.cs | 16 +-- .../Drawing/SolidComplexPolygonTests.cs | 9 +- .../Drawing/SolidPolygonTests.cs | 25 ++-- .../Processing/Effects/BackgroundColorTest.cs | 24 ++-- .../Processing/Overlays/GlowTest.cs | 16 +-- .../Processing/Overlays/VignetteTest.cs | 18 +-- .../Processors/Effects/BackgroundColorTest.cs | 4 +- .../Processors/Overlays/GlowTest.cs | 6 +- .../Processors/Overlays/OverlayTestBase.cs | 13 +- .../Processors/Overlays/VignetteTest.cs | 6 +- 30 files changed, 652 insertions(+), 508 deletions(-) create mode 100644 src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs create mode 100644 src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs create mode 100644 src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs diff --git a/src/ImageSharp/Color/Color.cs b/src/ImageSharp/Color/Color.cs index bc97393cc1..021cea4159 100644 --- a/src/ImageSharp/Color/Color.cs +++ b/src/ImageSharp/Color/Color.cs @@ -86,6 +86,8 @@ namespace SixLabors.ImageSharp public static Color FromRgba(byte r, byte g, byte b, byte a) => new Color(new Rgba32(r, g, b, a)); + public static Color FromRgb(byte r, byte g, byte b) => FromRgba(r, g, b, 255); + /// /// Creates a new instance from the string representing a color in hexadecimal form. /// diff --git a/src/ImageSharp/Primitives/ValueSize.cs b/src/ImageSharp/Primitives/ValueSize.cs index 44bee50309..577e9187a6 100644 --- a/src/ImageSharp/Primitives/ValueSize.cs +++ b/src/ImageSharp/Primitives/ValueSize.cs @@ -7,7 +7,7 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Primitives { /// - /// Represents a value in relation to a value on the image + /// Represents a value in relation to a value on the image. /// internal readonly struct ValueSize : IEquatable { diff --git a/src/ImageSharp/Processing/BackgroundColorExtensions.cs b/src/ImageSharp/Processing/BackgroundColorExtensions.cs index 3b794e3351..dd1cc1ed24 100644 --- a/src/ImageSharp/Processing/BackgroundColorExtensions.cs +++ b/src/ImageSharp/Processing/BackgroundColorExtensions.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Overlays; using SixLabors.Primitives; @@ -16,53 +15,55 @@ namespace SixLabors.ImageSharp.Processing /// /// Replaces the background color of image with the given one. /// - /// The pixel format. /// The image this method extends. /// The color to set as the background. - /// The . - public static IImageProcessingContext BackgroundColor(this IImageProcessingContext source, TPixel color) - where TPixel : struct, IPixel - => BackgroundColor(source, GraphicsOptions.Default, color); + /// The to allow chaining of operations. + public static IImageProcessingContext BackgroundColor(this IImageProcessingContext source, Color color) => + BackgroundColor(source, GraphicsOptions.Default, color); /// /// Replaces the background color of image with the given one. /// - /// The pixel format. /// The image this method extends. /// The color to set as the background. /// /// The structure that specifies the portion of the image object to alter. /// - /// The . - public static IImageProcessingContext BackgroundColor(this IImageProcessingContext source, TPixel color, Rectangle rectangle) - where TPixel : struct, IPixel - => BackgroundColor(source, GraphicsOptions.Default, color, rectangle); + /// The to allow chaining of operations. + public static IImageProcessingContext BackgroundColor( + this IImageProcessingContext source, + Color color, + Rectangle rectangle) => + BackgroundColor(source, GraphicsOptions.Default, color, rectangle); /// /// Replaces the background color of image with the given one. /// - /// The pixel format. /// The image this method extends. /// The options effecting pixel blending. /// The color to set as the background. - /// The . - public static IImageProcessingContext BackgroundColor(this IImageProcessingContext source, GraphicsOptions options, TPixel color) - where TPixel : struct, IPixel - => source.ApplyProcessor(new BackgroundColorProcessor(color, options)); + /// The to allow chaining of operations. + public static IImageProcessingContext BackgroundColor( + this IImageProcessingContext source, + GraphicsOptions options, + Color color) => + source.ApplyProcessor(new BackgroundColorProcessor(color, options)); /// /// Replaces the background color of image with the given one. /// - /// The pixel format. /// The image this method extends. /// The options effecting pixel blending. /// The color to set as the background. /// /// The structure that specifies the portion of the image object to alter. /// - /// The . - public static IImageProcessingContext BackgroundColor(this IImageProcessingContext source, GraphicsOptions options, TPixel color, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new BackgroundColorProcessor(color, options), rectangle); + /// The to allow chaining of operations. + public static IImageProcessingContext BackgroundColor( + this IImageProcessingContext source, + GraphicsOptions options, + Color color, + Rectangle rectangle) => + source.ApplyProcessor(new BackgroundColorProcessor(color, options), rectangle); } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/GlowExtensions.cs b/src/ImageSharp/Processing/GlowExtensions.cs index 759fdccbea..39734882b0 100644 --- a/src/ImageSharp/Processing/GlowExtensions.cs +++ b/src/ImageSharp/Processing/GlowExtensions.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing.Processors.Overlays; using SixLabors.Primitives; @@ -17,22 +16,18 @@ namespace SixLabors.ImageSharp.Processing /// /// Applies a radial glow effect to an image. /// - /// The pixel format. /// The image this method extends. /// The to allow chaining of operations. - public static IImageProcessingContext Glow(this IImageProcessingContext source) - where TPixel : struct, IPixel - => Glow(source, GraphicsOptions.Default); + public static IImageProcessingContext Glow(this IImageProcessingContext source) => + Glow(source, GraphicsOptions.Default); /// /// Applies a radial glow effect to an image. /// - /// The pixel format. /// The image this method extends. /// The color to set as the glow. /// The to allow chaining of operations. - public static IImageProcessingContext Glow(this IImageProcessingContext source, TPixel color) - where TPixel : struct, IPixel + public static IImageProcessingContext Glow(this IImageProcessingContext source, Color color) { return Glow(source, GraphicsOptions.Default, color); } @@ -40,31 +35,26 @@ namespace SixLabors.ImageSharp.Processing /// /// Applies a radial glow effect to an image. /// - /// The pixel format. /// The image this method extends. /// The the radius. /// The to allow chaining of operations. - public static IImageProcessingContext Glow(this IImageProcessingContext source, float radius) - where TPixel : struct, IPixel - => Glow(source, GraphicsOptions.Default, radius); + public static IImageProcessingContext Glow(this IImageProcessingContext source, float radius) => + Glow(source, GraphicsOptions.Default, radius); /// /// Applies a radial glow effect to an image. /// - /// The pixel format. /// The image this method extends. /// /// The structure that specifies the portion of the image object to alter. /// /// The to allow chaining of operations. - public static IImageProcessingContext Glow(this IImageProcessingContext source, Rectangle rectangle) - where TPixel : struct, IPixel - => source.Glow(GraphicsOptions.Default, rectangle); + public static IImageProcessingContext Glow(this IImageProcessingContext source, Rectangle rectangle) => + source.Glow(GraphicsOptions.Default, rectangle); /// /// Applies a radial glow effect to an image. /// - /// The pixel format. /// The image this method extends. /// The color to set as the glow. /// The the radius. @@ -72,63 +62,66 @@ namespace SixLabors.ImageSharp.Processing /// The structure that specifies the portion of the image object to alter. /// /// The to allow chaining of operations. - public static IImageProcessingContext Glow(this IImageProcessingContext source, TPixel color, float radius, Rectangle rectangle) - where TPixel : struct, IPixel - => source.Glow(GraphicsOptions.Default, color, ValueSize.Absolute(radius), rectangle); + public static IImageProcessingContext Glow( + this IImageProcessingContext source, + Color color, + float radius, + Rectangle rectangle) => + source.Glow(GraphicsOptions.Default, color, ValueSize.Absolute(radius), rectangle); /// /// Applies a radial glow effect to an image. /// - /// The pixel format. /// The image this method extends. /// The options effecting things like blending. /// The to allow chaining of operations. - public static IImageProcessingContext Glow(this IImageProcessingContext source, GraphicsOptions options) - where TPixel : struct, IPixel - => source.Glow(options, NamedColors.Black, ValueSize.PercentageOfWidth(0.5f)); + public static IImageProcessingContext Glow(this IImageProcessingContext source, GraphicsOptions options) => + source.Glow(options, Color.Black, ValueSize.PercentageOfWidth(0.5f)); /// /// Applies a radial glow effect to an image. /// - /// The pixel format. /// The image this method extends. /// The options effecting things like blending. /// The color to set as the glow. /// The to allow chaining of operations. - public static IImageProcessingContext Glow(this IImageProcessingContext source, GraphicsOptions options, TPixel color) - where TPixel : struct, IPixel - => source.Glow(options, color, ValueSize.PercentageOfWidth(0.5f)); + public static IImageProcessingContext Glow( + this IImageProcessingContext source, + GraphicsOptions options, + Color color) => + source.Glow(options, color, ValueSize.PercentageOfWidth(0.5f)); /// /// Applies a radial glow effect to an image. /// - /// The pixel format. /// The image this method extends. /// The options effecting things like blending. /// The the radius. /// The to allow chaining of operations. - public static IImageProcessingContext Glow(this IImageProcessingContext source, GraphicsOptions options, float radius) - where TPixel : struct, IPixel - => source.Glow(options, NamedColors.Black, ValueSize.Absolute(radius)); + public static IImageProcessingContext Glow( + this IImageProcessingContext source, + GraphicsOptions options, + float radius) => + source.Glow(options, Color.Black, ValueSize.Absolute(radius)); /// /// Applies a radial glow effect to an image. /// - /// The pixel format. /// The image this method extends. /// The options effecting things like blending. /// /// The structure that specifies the portion of the image object to alter. /// /// The to allow chaining of operations. - public static IImageProcessingContext Glow(this IImageProcessingContext source, GraphicsOptions options, Rectangle rectangle) - where TPixel : struct, IPixel - => source.Glow(options, NamedColors.Black, ValueSize.PercentageOfWidth(0.5f), rectangle); + public static IImageProcessingContext Glow( + this IImageProcessingContext source, + GraphicsOptions options, + Rectangle rectangle) => + source.Glow(options, Color.Black, ValueSize.PercentageOfWidth(0.5f), rectangle); /// /// Applies a radial glow effect to an image. /// - /// The pixel format. /// The image this method extends. /// The options effecting things like blending. /// The color to set as the glow. @@ -137,14 +130,17 @@ namespace SixLabors.ImageSharp.Processing /// The structure that specifies the portion of the image object to alter. /// /// The to allow chaining of operations. - public static IImageProcessingContext Glow(this IImageProcessingContext source, GraphicsOptions options, TPixel color, float radius, Rectangle rectangle) - where TPixel : struct, IPixel - => source.Glow(options, color, ValueSize.Absolute(radius), rectangle); + public static IImageProcessingContext Glow( + this IImageProcessingContext source, + GraphicsOptions options, + Color color, + float radius, + Rectangle rectangle) => + source.Glow(options, color, ValueSize.Absolute(radius), rectangle); /// /// Applies a radial glow effect to an image. /// - /// The pixel format. /// The image this method extends. /// The options effecting things like blending. /// The color to set as the glow. @@ -153,21 +149,27 @@ namespace SixLabors.ImageSharp.Processing /// The structure that specifies the portion of the image object to alter. /// /// The to allow chaining of operations. - private static IImageProcessingContext Glow(this IImageProcessingContext source, GraphicsOptions options, TPixel color, ValueSize radius, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new GlowProcessor(color, radius, options), rectangle); + private static IImageProcessingContext Glow( + this IImageProcessingContext source, + GraphicsOptions options, + Color color, + ValueSize radius, + Rectangle rectangle) => + source.ApplyProcessor(new GlowProcessor(color, radius, options), rectangle); /// /// Applies a radial glow effect to an image. /// - /// The pixel format. /// The image this method extends. /// The options effecting things like blending. /// The color to set as the glow. /// The the radius. /// The to allow chaining of operations. - private static IImageProcessingContext Glow(this IImageProcessingContext source, GraphicsOptions options, TPixel color, ValueSize radius) - where TPixel : struct, IPixel - => source.ApplyProcessor(new GlowProcessor(color, radius, options)); + private static IImageProcessingContext Glow( + this IImageProcessingContext source, + GraphicsOptions options, + Color color, + ValueSize radius) => + source.ApplyProcessor(new GlowProcessor(color, radius, options)); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs index c15cf6fc90..e0f85945aa 100644 --- a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters internal class LomographProcessor : FilterProcessor where TPixel : struct, IPixel { - private static readonly TPixel VeryDarkGreen = ColorBuilder.FromRGBA(0, 10, 0, 255); + private static readonly Color VeryDarkGreen = Color.FromRgba(0, 10, 0, 255); /// /// Initializes a new instance of the class. @@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// protected override void AfterFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - new VignetteProcessor(VeryDarkGreen).Apply(source, sourceRectangle, configuration); + new VignetteProcessor(VeryDarkGreen).Apply(source, sourceRectangle, configuration); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs index befcfc1f42..0f511ee296 100644 --- a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs @@ -13,8 +13,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters internal class PolaroidProcessor : FilterProcessor where TPixel : struct, IPixel { - private static readonly TPixel VeryDarkOrange = ColorBuilder.FromRGB(102, 34, 0); - private static readonly TPixel LightOrange = ColorBuilder.FromRGBA(255, 153, 102, 128); + private static readonly Color LightOrange = Color.FromRgba(255, 153, 102, 128); + + private static readonly Color VeryDarkOrange = Color.FromRgb(102, 34, 0); /// /// Initializes a new instance of the class. @@ -26,10 +27,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters } /// - protected override void AfterFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + protected override void AfterFrameApply( + ImageFrame source, + Rectangle sourceRectangle, + Configuration configuration) { - new VignetteProcessor(VeryDarkOrange).Apply(source, sourceRectangle, configuration); - new GlowProcessor(LightOrange, source.Width / 4F).Apply(source, sourceRectangle, configuration); + new VignetteProcessor(VeryDarkOrange).Apply(source, sourceRectangle, configuration); + new GlowProcessor(LightOrange, source.Width / 4F).Apply(source, sourceRectangle, configuration); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor.cs index 25787ff922..0dd0c41188 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor.cs @@ -1,31 +1,21 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; -using System.Buffers; -using System.Threading.Tasks; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Overlays { /// - /// Sets the background color of the image. + /// Defines a processing operation to replace the background color of an . /// - /// The pixel format. - internal class BackgroundColorProcessor : ImageProcessor - where TPixel : struct, IPixel + public sealed class BackgroundColorProcessor : IImageProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// The to set the background color to. + /// The to set the background color to. /// The options defining blending algorithm and amount. - public BackgroundColorProcessor(TPixel color, GraphicsOptions options) + public BackgroundColorProcessor(Color color, GraphicsOptions options) { this.Color = color; this.GraphicsOptions = options; @@ -39,69 +29,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays /// /// Gets the background color value. /// - public TPixel Color { get; } + public Color Color { get; } - /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + /// + public IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel { - int startY = sourceRectangle.Y; - int endY = sourceRectangle.Bottom; - int startX = sourceRectangle.X; - int endX = sourceRectangle.Right; - - // Align start/end positions. - int minX = Math.Max(0, startX); - int maxX = Math.Min(source.Width, endX); - int minY = Math.Max(0, startY); - int maxY = Math.Min(source.Height, endY); - - // Reset offset if necessary. - if (minX > 0) - { - startX = 0; - } - - if (minY > 0) - { - startY = 0; - } - - int width = maxX - minX; - - var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY); - - using (IMemoryOwner colors = source.MemoryAllocator.Allocate(width)) - using (IMemoryOwner amount = source.MemoryAllocator.Allocate(width)) - { - // Be careful! Do not capture colorSpan & amountSpan in the lambda below! - Span colorSpan = colors.GetSpan(); - Span amountSpan = amount.GetSpan(); - - colorSpan.Fill(this.Color); - amountSpan.Fill(this.GraphicsOptions.BlendPercentage); - - PixelBlender blender = PixelOperations.Instance.GetPixelBlender(this.GraphicsOptions); - - ParallelHelper.IterateRows( - workingRect, - configuration, - rows => - { - for (int y = rows.Min; y < rows.Max; y++) - { - Span destination = - source.GetPixelRowSpan(y - startY).Slice(minX - startX, width); - - // This switched color & destination in the 2nd and 3rd places because we are applying the target color under the current one - blender.Blend( - source.Configuration, - destination, - colors.GetSpan(), - destination, - amount.GetSpan()); - } - }); - } + return new BackgroundColorProcessor(this); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs new file mode 100644 index 0000000000..c4af59fecb --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs @@ -0,0 +1,101 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Overlays +{ + /// + /// Sets the background color of the image. + /// + /// The pixel format. + internal class BackgroundColorProcessor : ImageProcessor + where TPixel : struct, IPixel + { + private readonly BackgroundColorProcessor definition; + + /// + /// Initializes a new instance of the class. + /// + public BackgroundColorProcessor(BackgroundColorProcessor definition) + { + this.definition = definition; + } + + /// + protected override void OnFrameApply( + ImageFrame source, + Rectangle sourceRectangle, + Configuration configuration) + { + TPixel color = this.definition.Color.ToPixel(); + GraphicsOptions graphicsOptions = this.definition.GraphicsOptions; + + int startY = sourceRectangle.Y; + int endY = sourceRectangle.Bottom; + int startX = sourceRectangle.X; + int endX = sourceRectangle.Right; + + // Align start/end positions. + int minX = Math.Max(0, startX); + int maxX = Math.Min(source.Width, endX); + int minY = Math.Max(0, startY); + int maxY = Math.Min(source.Height, endY); + + // Reset offset if necessary. + if (minX > 0) + { + startX = 0; + } + + if (minY > 0) + { + startY = 0; + } + + int width = maxX - minX; + + var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY); + + using (IMemoryOwner colors = source.MemoryAllocator.Allocate(width)) + using (IMemoryOwner amount = source.MemoryAllocator.Allocate(width)) + { + // Be careful! Do not capture colorSpan & amountSpan in the lambda below! + Span colorSpan = colors.GetSpan(); + Span amountSpan = amount.GetSpan(); + + colorSpan.Fill(color); + amountSpan.Fill(graphicsOptions.BlendPercentage); + + PixelBlender blender = PixelOperations.Instance.GetPixelBlender(graphicsOptions); + + ParallelHelper.IterateRows( + workingRect, + configuration, + rows => + { + for (int y = rows.Min; y < rows.Max; y++) + { + Span destination = + source.GetPixelRowSpan(y - startY).Slice(minX - startX, width); + + // This switched color & destination in the 2nd and 3rd places because we are applying the target color under the current one + blender.Blend( + source.Configuration, + destination, + colors.GetSpan(), + destination, + amount.GetSpan()); + } + }); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs index 21f6be69f8..4927a0ed7a 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs @@ -1,69 +1,56 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; -using System.Buffers; -using System.Numerics; -using System.Threading.Tasks; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; -using SixLabors.Memory; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Overlays { /// - /// An that applies a radial glow effect an . + /// Defines a radial glow effect applicable to an . /// - /// The pixel format. - internal class GlowProcessor : ImageProcessor - where TPixel : struct, IPixel + public sealed class GlowProcessor : IImageProcessor { - private readonly PixelBlender blender; - /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The color or the glow. - public GlowProcessor(TPixel color) + public GlowProcessor(Color color) : this(color, 0) { } + /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The color or the glow. - /// The radius of the glow. - public GlowProcessor(TPixel color, ValueSize radius) - : this(color, radius, GraphicsOptions.Default) + /// The options effecting blending and composition. + public GlowProcessor(Color color, GraphicsOptions options) + : this(color, 0, options) { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The color or the glow. - /// The options effecting blending and composition. - public GlowProcessor(TPixel color, GraphicsOptions options) - : this(color, 0, options) + /// The radius of the glow. + internal GlowProcessor(Color color, ValueSize radius) + : this(color, radius, GraphicsOptions.Default) { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The color or the glow. /// The radius of the glow. /// The options effecting blending and composition. - public GlowProcessor(TPixel color, ValueSize radius, GraphicsOptions options) + internal GlowProcessor(Color color, ValueSize radius, GraphicsOptions options) { this.GlowColor = color; this.Radius = radius; - this.blender = PixelOperations.Instance.GetPixelBlender(options); this.GraphicsOptions = options; } @@ -73,86 +60,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays public GraphicsOptions GraphicsOptions { get; } /// - /// Gets or sets the glow color to apply. + /// Gets the glow color to apply. /// - public TPixel GlowColor { get; set; } + public Color GlowColor { get; } /// - /// Gets or sets the the radius. + /// Gets the the radius. /// - public ValueSize Radius { get; set; } + internal ValueSize Radius { get; } - /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + /// + public IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel { - // TODO: can we simplify the rectangle calculation? - int startY = sourceRectangle.Y; - int endY = sourceRectangle.Bottom; - int startX = sourceRectangle.X; - int endX = sourceRectangle.Right; - TPixel glowColor = this.GlowColor; - Vector2 center = Rectangle.Center(sourceRectangle); - - float finalRadius = this.Radius.Calculate(source.Size()); - - float maxDistance = finalRadius > 0 ? MathF.Min(finalRadius, sourceRectangle.Width * .5F) : sourceRectangle.Width * .5F; - - // Align start/end positions. - int minX = Math.Max(0, startX); - int maxX = Math.Min(source.Width, endX); - int minY = Math.Max(0, startY); - int maxY = Math.Min(source.Height, endY); - - // Reset offset if necessary. - if (minX > 0) - { - startX = 0; - } - - if (minY > 0) - { - startY = 0; - } - - int width = maxX - minX; - int offsetX = minX - startX; - - var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY); - - using (IMemoryOwner rowColors = source.MemoryAllocator.Allocate(width)) - { - rowColors.GetSpan().Fill(glowColor); - - ParallelHelper.IterateRowsWithTempBuffer( - workingRect, - configuration, - (rows, amounts) => - { - Span amountsSpan = amounts.Span; - - for (int y = rows.Min; y < rows.Max; y++) - { - int offsetY = y - startY; - - for (int i = 0; i < width; i++) - { - float distance = Vector2.Distance(center, new Vector2(i + offsetX, offsetY)); - amountsSpan[i] = - (this.GraphicsOptions.BlendPercentage * (1 - (.95F * (distance / maxDistance)))) - .Clamp(0, 1); - } - - Span destination = source.GetPixelRowSpan(offsetY).Slice(offsetX, width); - - this.blender.Blend( - source.Configuration, - destination, - destination, - rowColors.GetSpan(), - amountsSpan); - } - }); - } + return new GlowProcessor(this); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs new file mode 100644 index 0000000000..3201fcbfee --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs @@ -0,0 +1,112 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; +using System.Numerics; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Overlays +{ + /// + /// An that applies a radial glow effect an . + /// + /// The pixel format. + internal class GlowProcessor : ImageProcessor + where TPixel : struct, IPixel + { + private readonly PixelBlender blender; + + private readonly GlowProcessor definition; + + public GlowProcessor(GlowProcessor definition) + { + this.definition = definition; + this.blender = PixelOperations.Instance.GetPixelBlender(definition.GraphicsOptions); + } + + /// + protected override void OnFrameApply( + ImageFrame source, + Rectangle sourceRectangle, + Configuration configuration) + { + // TODO: can we simplify the rectangle calculation? + int startY = sourceRectangle.Y; + int endY = sourceRectangle.Bottom; + int startX = sourceRectangle.X; + int endX = sourceRectangle.Right; + TPixel glowColor = this.definition.GlowColor.ToPixel(); + Vector2 center = Rectangle.Center(sourceRectangle); + + float finalRadius = this.definition.Radius.Calculate(source.Size()); + + float maxDistance = finalRadius > 0 + ? MathF.Min(finalRadius, sourceRectangle.Width * .5F) + : sourceRectangle.Width * .5F; + + // Align start/end positions. + int minX = Math.Max(0, startX); + int maxX = Math.Min(source.Width, endX); + int minY = Math.Max(0, startY); + int maxY = Math.Min(source.Height, endY); + + // Reset offset if necessary. + if (minX > 0) + { + startX = 0; + } + + if (minY > 0) + { + startY = 0; + } + + int width = maxX - minX; + int offsetX = minX - startX; + + var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY); + + float blendPercentage = this.definition.GraphicsOptions.BlendPercentage; + + using (IMemoryOwner rowColors = source.MemoryAllocator.Allocate(width)) + { + rowColors.GetSpan().Fill(glowColor); + + ParallelHelper.IterateRowsWithTempBuffer( + workingRect, + configuration, + (rows, amounts) => + { + Span amountsSpan = amounts.Span; + + for (int y = rows.Min; y < rows.Max; y++) + { + int offsetY = y - startY; + + for (int i = 0; i < width; i++) + { + float distance = Vector2.Distance(center, new Vector2(i + offsetX, offsetY)); + amountsSpan[i] = + (blendPercentage * (1 - (.95F * (distance / maxDistance)))).Clamp(0, 1); + } + + Span destination = source.GetPixelRowSpan(offsetY).Slice(offsetX, width); + + this.blender.Blend( + source.Configuration, + destination, + destination, + rowColors.GetSpan(), + amountsSpan); + } + }); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs index a8fa1d65c1..66bf9f1af7 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs @@ -1,63 +1,48 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; -using System.Buffers; -using System.Numerics; -using System.Threading.Tasks; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; -using SixLabors.Memory; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Overlays { /// - /// An that applies a radial vignette effect to an . + /// Defines a radial vignette effect applicable to an . /// - /// The pixel format. - internal class VignetteProcessor : ImageProcessor - where TPixel : struct, IPixel + public sealed class VignetteProcessor : IImageProcessor { - private readonly PixelBlender blender; - /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The color of the vignette. - public VignetteProcessor(TPixel color) + public VignetteProcessor(Color color) : this(color, GraphicsOptions.Default) { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The color of the vignette. /// The options effecting blending and composition. - public VignetteProcessor(TPixel color, GraphicsOptions options) + public VignetteProcessor(Color color, GraphicsOptions options) { this.VignetteColor = color; this.GraphicsOptions = options; - this.blender = PixelOperations.Instance.GetPixelBlender(options); } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The color of the vignette. /// The x-radius. /// The y-radius. /// The options effecting blending and composition. - public VignetteProcessor(TPixel color, ValueSize radiusX, ValueSize radiusY, GraphicsOptions options) + internal VignetteProcessor(Color color, ValueSize radiusX, ValueSize radiusY, GraphicsOptions options) { this.VignetteColor = color; this.RadiusX = radiusX; this.RadiusY = radiusY; - this.blender = PixelOperations.Instance.GetPixelBlender(options); this.GraphicsOptions = options; } @@ -67,94 +52,25 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays public GraphicsOptions GraphicsOptions { get; } /// - /// Gets or sets the vignette color to apply. + /// Gets the vignette color to apply. /// - public TPixel VignetteColor { get; set; } + public Color VignetteColor { get; } /// - /// Gets or sets the the x-radius. + /// Gets the the x-radius. /// - public ValueSize RadiusX { get; set; } + internal ValueSize RadiusX { get; } /// - /// Gets or sets the the y-radius. + /// Gets the the y-radius. /// - public ValueSize RadiusY { get; set; } + internal ValueSize RadiusY { get; } - /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + /// + public IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel { - int startY = sourceRectangle.Y; - int endY = sourceRectangle.Bottom; - int startX = sourceRectangle.X; - int endX = sourceRectangle.Right; - TPixel vignetteColor = this.VignetteColor; - Vector2 centre = Rectangle.Center(sourceRectangle); - - Size sourceSize = source.Size(); - float finalRadiusX = this.RadiusX.Calculate(sourceSize); - float finalRadiusY = this.RadiusY.Calculate(sourceSize); - float rX = finalRadiusX > 0 ? MathF.Min(finalRadiusX, sourceRectangle.Width * .5F) : sourceRectangle.Width * .5F; - float rY = finalRadiusY > 0 ? MathF.Min(finalRadiusY, sourceRectangle.Height * .5F) : sourceRectangle.Height * .5F; - float maxDistance = MathF.Sqrt((rX * rX) + (rY * rY)); - - // Align start/end positions. - int minX = Math.Max(0, startX); - int maxX = Math.Min(source.Width, endX); - int minY = Math.Max(0, startY); - int maxY = Math.Min(source.Height, endY); - - // Reset offset if necessary. - if (minX > 0) - { - startX = 0; - } - - if (minY > 0) - { - startY = 0; - } - - int width = maxX - minX; - int offsetX = minX - startX; - - var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY); - - using (IMemoryOwner rowColors = source.MemoryAllocator.Allocate(width)) - { - rowColors.GetSpan().Fill(vignetteColor); - - ParallelHelper.IterateRowsWithTempBuffer( - workingRect, - configuration, - (rows, amounts) => - { - Span amountsSpan = amounts.Span; - - for (int y = rows.Min; y < rows.Max; y++) - { - int offsetY = y - startY; - - for (int i = 0; i < width; i++) - { - float distance = Vector2.Distance(centre, new Vector2(i + offsetX, offsetY)); - amountsSpan[i] = - (this.GraphicsOptions.BlendPercentage * (.9F * (distance / maxDistance))).Clamp( - 0, - 1); - } - - Span destination = source.GetPixelRowSpan(offsetY).Slice(offsetX, width); - - this.blender.Blend( - source.Configuration, - destination, - destination, - rowColors.GetSpan(), - amountsSpan); - } - }); - } + return new VignetteProcessor(this); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs new file mode 100644 index 0000000000..78c3cec5e7 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs @@ -0,0 +1,114 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; +using System.Numerics; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Overlays +{ + /// + /// An that applies a radial vignette effect to an . + /// + /// The pixel format. + internal class VignetteProcessor : ImageProcessor + where TPixel : struct, IPixel + { + private readonly PixelBlender blender; + + private readonly VignetteProcessor definition; + + public VignetteProcessor(VignetteProcessor definition) + { + this.definition = definition; + this.blender = PixelOperations.Instance.GetPixelBlender(definition.GraphicsOptions); + } + + /// + protected override void OnFrameApply( + ImageFrame source, + Rectangle sourceRectangle, + Configuration configuration) + { + int startY = sourceRectangle.Y; + int endY = sourceRectangle.Bottom; + int startX = sourceRectangle.X; + int endX = sourceRectangle.Right; + TPixel vignetteColor = this.definition.VignetteColor.ToPixel(); + Vector2 centre = Rectangle.Center(sourceRectangle); + + Size sourceSize = source.Size(); + float finalRadiusX = this.definition.RadiusX.Calculate(sourceSize); + float finalRadiusY = this.definition.RadiusY.Calculate(sourceSize); + float rX = finalRadiusX > 0 + ? MathF.Min(finalRadiusX, sourceRectangle.Width * .5F) + : sourceRectangle.Width * .5F; + float rY = finalRadiusY > 0 + ? MathF.Min(finalRadiusY, sourceRectangle.Height * .5F) + : sourceRectangle.Height * .5F; + float maxDistance = MathF.Sqrt((rX * rX) + (rY * rY)); + + // Align start/end positions. + int minX = Math.Max(0, startX); + int maxX = Math.Min(source.Width, endX); + int minY = Math.Max(0, startY); + int maxY = Math.Min(source.Height, endY); + + // Reset offset if necessary. + if (minX > 0) + { + startX = 0; + } + + if (minY > 0) + { + startY = 0; + } + + int width = maxX - minX; + int offsetX = minX - startX; + + var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY); + float blendPercentage = this.definition.GraphicsOptions.BlendPercentage; + + using (IMemoryOwner rowColors = source.MemoryAllocator.Allocate(width)) + { + rowColors.GetSpan().Fill(vignetteColor); + + ParallelHelper.IterateRowsWithTempBuffer( + workingRect, + configuration, + (rows, amounts) => + { + Span amountsSpan = amounts.Span; + + for (int y = rows.Min; y < rows.Max; y++) + { + int offsetY = y - startY; + + for (int i = 0; i < width; i++) + { + float distance = Vector2.Distance(centre, new Vector2(i + offsetX, offsetY)); + amountsSpan[i] = (blendPercentage * (.9F * (distance / maxDistance))).Clamp(0, 1); + } + + Span destination = source.GetPixelRowSpan(offsetY).Slice(offsetX, width); + + this.blender.Blend( + source.Configuration, + destination, + destination, + rowColors.GetSpan(), + amountsSpan); + } + }); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/VignetteExtensions.cs b/src/ImageSharp/Processing/VignetteExtensions.cs index 63cdee3f83..74a59d3e13 100644 --- a/src/ImageSharp/Processing/VignetteExtensions.cs +++ b/src/ImageSharp/Processing/VignetteExtensions.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing.Processors.Overlays; using SixLabors.Primitives; @@ -9,7 +8,7 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Defines extensions that allow the application of a radial glow to an + /// Defines extensions that allow the application of a radial glow to an /// using Mutate/Clone. /// public static class VignetteExtensions @@ -17,53 +16,47 @@ namespace SixLabors.ImageSharp.Processing /// /// Applies a radial vignette effect to an image. /// - /// The pixel format. /// The image this method extends. /// The to allow chaining of operations. - public static IImageProcessingContext Vignette(this IImageProcessingContext source) - where TPixel : struct, IPixel - => Vignette(source, GraphicsOptions.Default); + public static IImageProcessingContext Vignette(this IImageProcessingContext source) => + Vignette(source, GraphicsOptions.Default); /// /// Applies a radial vignette effect to an image. /// - /// The pixel format. /// The image this method extends. /// The color to set as the vignette. /// The to allow chaining of operations. - public static IImageProcessingContext Vignette(this IImageProcessingContext source, TPixel color) - where TPixel : struct, IPixel - => Vignette(source, GraphicsOptions.Default, color); + public static IImageProcessingContext Vignette(this IImageProcessingContext source, Color color) => + Vignette(source, GraphicsOptions.Default, color); /// /// Applies a radial vignette effect to an image. /// - /// The pixel format. /// The image this method extends. /// The the x-radius. /// The the y-radius. /// The to allow chaining of operations. - public static IImageProcessingContext Vignette(this IImageProcessingContext source, float radiusX, float radiusY) - where TPixel : struct, IPixel - => Vignette(source, GraphicsOptions.Default, radiusX, radiusY); + public static IImageProcessingContext Vignette( + this IImageProcessingContext source, + float radiusX, + float radiusY) => + Vignette(source, GraphicsOptions.Default, radiusX, radiusY); /// /// Applies a radial vignette effect to an image. /// - /// The pixel format. /// The image this method extends. /// /// The structure that specifies the portion of the image object to alter. /// /// The to allow chaining of operations. - public static IImageProcessingContext Vignette(this IImageProcessingContext source, Rectangle rectangle) - where TPixel : struct, IPixel - => Vignette(source, GraphicsOptions.Default, rectangle); + public static IImageProcessingContext Vignette(this IImageProcessingContext source, Rectangle rectangle) => + Vignette(source, GraphicsOptions.Default, rectangle); /// /// Applies a radial vignette effect to an image. /// - /// The pixel format. /// The image this method extends. /// The color to set as the vignette. /// The the x-radius. @@ -72,64 +65,82 @@ namespace SixLabors.ImageSharp.Processing /// The structure that specifies the portion of the image object to alter. /// /// The to allow chaining of operations. - public static IImageProcessingContext Vignette(this IImageProcessingContext source, TPixel color, float radiusX, float radiusY, Rectangle rectangle) - where TPixel : struct, IPixel - => source.Vignette(GraphicsOptions.Default, color, radiusX, radiusY, rectangle); + public static IImageProcessingContext Vignette( + this IImageProcessingContext source, + Color color, + float radiusX, + float radiusY, + Rectangle rectangle) => + source.Vignette(GraphicsOptions.Default, color, radiusX, radiusY, rectangle); /// /// Applies a radial vignette effect to an image. /// - /// The pixel format. /// The image this method extends. /// The options effecting pixel blending. /// The to allow chaining of operations. - public static IImageProcessingContext Vignette(this IImageProcessingContext source, GraphicsOptions options) - where TPixel : struct, IPixel - => source.VignetteInternal(options, NamedColors.Black, ValueSize.PercentageOfWidth(.5f), ValueSize.PercentageOfHeight(.5f)); + public static IImageProcessingContext Vignette(this IImageProcessingContext source, GraphicsOptions options) => + source.VignetteInternal( + options, + Color.Black, + ValueSize.PercentageOfWidth(.5f), + ValueSize.PercentageOfHeight(.5f)); /// /// Applies a radial vignette effect to an image. /// - /// The pixel format. /// The image this method extends. /// The options effecting pixel blending. /// The color to set as the vignette. /// The to allow chaining of operations. - public static IImageProcessingContext Vignette(this IImageProcessingContext source, GraphicsOptions options, TPixel color) - where TPixel : struct, IPixel - => source.VignetteInternal(options, color, ValueSize.PercentageOfWidth(.5f), ValueSize.PercentageOfHeight(.5f)); + public static IImageProcessingContext Vignette( + this IImageProcessingContext source, + GraphicsOptions options, + Color color) => + source.VignetteInternal( + options, + color, + ValueSize.PercentageOfWidth(.5f), + ValueSize.PercentageOfHeight(.5f)); /// /// Applies a radial vignette effect to an image. /// - /// The pixel format. /// The image this method extends. /// The options effecting pixel blending. /// The the x-radius. /// The the y-radius. /// The to allow chaining of operations. - public static IImageProcessingContext Vignette(this IImageProcessingContext source, GraphicsOptions options, float radiusX, float radiusY) - where TPixel : struct, IPixel - => source.VignetteInternal(options, NamedColors.Black, radiusX, radiusY); + public static IImageProcessingContext Vignette( + this IImageProcessingContext source, + GraphicsOptions options, + float radiusX, + float radiusY) => + source.VignetteInternal(options, Color.Black, radiusX, radiusY); /// /// Applies a radial vignette effect to an image. /// - /// The pixel format. /// The image this method extends. /// The options effecting pixel blending. /// /// The structure that specifies the portion of the image object to alter. /// /// The to allow chaining of operations. - public static IImageProcessingContext Vignette(this IImageProcessingContext source, GraphicsOptions options, Rectangle rectangle) - where TPixel : struct, IPixel - => source.VignetteInternal(options, NamedColors.Black, ValueSize.PercentageOfWidth(.5f), ValueSize.PercentageOfHeight(.5f), rectangle); + public static IImageProcessingContext Vignette( + this IImageProcessingContext source, + GraphicsOptions options, + Rectangle rectangle) => + source.VignetteInternal( + options, + Color.Black, + ValueSize.PercentageOfWidth(.5f), + ValueSize.PercentageOfHeight(.5f), + rectangle); /// /// Applies a radial vignette effect to an image. /// - /// The pixel format. /// The image this method extends. /// The options effecting pixel blending. /// The color to set as the vignette. @@ -139,16 +150,30 @@ namespace SixLabors.ImageSharp.Processing /// The structure that specifies the portion of the image object to alter. /// /// The to allow chaining of operations. - public static IImageProcessingContext Vignette(this IImageProcessingContext source, GraphicsOptions options, TPixel color, float radiusX, float radiusY, Rectangle rectangle) - where TPixel : struct, IPixel - => source.VignetteInternal(options, color, radiusX, radiusY, rectangle); + public static IImageProcessingContext Vignette( + this IImageProcessingContext source, + GraphicsOptions options, + Color color, + float radiusX, + float radiusY, + Rectangle rectangle) => + source.VignetteInternal(options, color, radiusX, radiusY, rectangle); - private static IImageProcessingContext VignetteInternal(this IImageProcessingContext source, GraphicsOptions options, TPixel color, ValueSize radiusX, ValueSize radiusY, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new VignetteProcessor(color, radiusX, radiusY, options), rectangle); + private static IImageProcessingContext VignetteInternal( + this IImageProcessingContext source, + GraphicsOptions options, + Color color, + ValueSize radiusX, + ValueSize radiusY, + Rectangle rectangle) => + source.ApplyProcessor(new VignetteProcessor(color, radiusX, radiusY, options), rectangle); - private static IImageProcessingContext VignetteInternal(this IImageProcessingContext source, GraphicsOptions options, TPixel color, ValueSize radiusX, ValueSize radiusY) - where TPixel : struct, IPixel - => source.ApplyProcessor(new VignetteProcessor(color, radiusX, radiusY, options)); + private static IImageProcessingContext VignetteInternal( + this IImageProcessingContext source, + GraphicsOptions options, + Color color, + ValueSize radiusX, + ValueSize radiusY) => + source.ApplyProcessor(new VignetteProcessor(color, radiusX, radiusY, options)); } } \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Samplers/Glow.cs b/tests/ImageSharp.Benchmarks/Samplers/Glow.cs index 15b82e022d..29f69cdd55 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/Glow.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/Glow.cs @@ -20,14 +20,14 @@ namespace SixLabors.ImageSharp.Benchmarks public class Glow : BenchmarkBase { - private GlowProcessor bulk; + private GlowProcessor bulk; private GlowProcessorParallel parallel; [GlobalSetup] public void Setup() { - this.bulk = new GlowProcessor(NamedColors.Beige, 800 * .5f, GraphicsOptions.Default); + this.bulk = new GlowProcessor(Color.Beige, 800 * .5f, GraphicsOptions.Default); this.parallel = new GlowProcessorParallel(NamedColors.Beige) { Radius = 800 * .5f, }; } diff --git a/tests/ImageSharp.Tests/Drawing/BeziersTests.cs b/tests/ImageSharp.Tests/Drawing/BeziersTests.cs index 69b2098dc9..cc8f27f3a2 100644 --- a/tests/ImageSharp.Tests/Drawing/BeziersTests.cs +++ b/tests/ImageSharp.Tests/Drawing/BeziersTests.cs @@ -19,8 +19,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing string path = TestEnvironment.CreateOutputDirectory("Drawing", "BezierLine"); using (var image = new Image(500, 500)) { + image.Mutate(x => x.BackgroundColor(Color.Blue)); image.Mutate( - x => x.BackgroundColor(Rgba32.Blue).DrawBeziers( + x => x.DrawBeziers( Rgba32.HotPink, 5, new SixLabors.Primitives.PointF[] @@ -56,8 +57,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing using (var image = new Image(500, 500)) { + image.Mutate(x => x.BackgroundColor(Color.Blue)); image.Mutate( - x => x.BackgroundColor(Rgba32.Blue).DrawBeziers( + x => x.DrawBeziers( color, 10, new SixLabors.Primitives.PointF[] diff --git a/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs b/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs index ee04d43888..54b04390ec 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs @@ -80,7 +80,7 @@ namespace SixLabors.ImageSharp.Tests // Apply a background color so we can see the translation. blend.Mutate(x => x.Transform(builder)); - blend.Mutate(x => x.BackgroundColor(NamedColors.HotPink)); + blend.Mutate(x => x.BackgroundColor(Color.HotPink)); // Lets center the matrix so we can tell whether any cut-off issues we may have belong to the drawing processor var position = new Point((image.Width - blend.Width) / 2, (image.Height - blend.Height) / 2); diff --git a/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs b/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs index 0d791fbd23..d2485f334a 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs @@ -30,8 +30,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing new Vector2(10, 400)); var p = new Path(linerSegemnt, bazierSegment); - - image.Mutate(x => x.BackgroundColor(Rgba32.Blue).Draw(Rgba32.HotPink, 5, p)); + + image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); + image.Mutate(x => x.Draw(Rgba32.HotPink, 5, p)); image.Save($"{path}/Simple.png"); Buffer2D sourcePixels = image.GetRootFramePixelBuffer(); @@ -67,7 +68,8 @@ namespace SixLabors.ImageSharp.Tests.Drawing using (var image = new Image(500, 500)) { - image.Mutate(x => x.BackgroundColor(Rgba32.Blue).Draw(color, 10, p)); + image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); + image.Mutate(x => x.Draw(color, 10, p)); image.Save($"{path}/Opacity.png"); //shift background color towards forground color by the opacity amount diff --git a/tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs index d827975c72..06961bec4b 100644 --- a/tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs @@ -31,7 +31,8 @@ namespace SixLabors.ImageSharp.Tests.Drawing using (var image = new Image(500, 500)) { - image.Mutate(x => x.BackgroundColor(Rgba32.Blue).Draw(Rgba32.HotPink, 5, simplePath.Clip(hole1))); + image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); + image.Mutate(x => x.Draw(Rgba32.HotPink, 5, simplePath.Clip(hole1))); image.Save($"{path}/Simple.png"); Buffer2D sourcePixels = image.GetRootFramePixelBuffer(); @@ -73,7 +74,8 @@ namespace SixLabors.ImageSharp.Tests.Drawing using (var image = new Image(500, 500)) { - image.Mutate(x => x.BackgroundColor(Rgba32.Blue).Draw(Rgba32.HotPink, 5, simplePath.Clip(hole1))); + image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); + image.Mutate(x => x.Draw(Rgba32.HotPink, 5, simplePath.Clip(hole1))); image.Save($"{path}/SimpleVanishHole.png"); Buffer2D sourcePixels = image.GetRootFramePixelBuffer(); @@ -113,7 +115,8 @@ namespace SixLabors.ImageSharp.Tests.Drawing using (var image = new Image(500, 500)) { - image.Mutate(x => x.BackgroundColor(Rgba32.Blue).Draw(Rgba32.HotPink, 5, simplePath.Clip(hole1))); + image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); + image.Mutate(x => x.Draw(Rgba32.HotPink, 5, simplePath.Clip(hole1))); image.Save($"{path}/SimpleOverlapping.png"); Buffer2D sourcePixels = image.GetRootFramePixelBuffer(); @@ -147,9 +150,8 @@ namespace SixLabors.ImageSharp.Tests.Drawing using (var image = new Image(500, 500)) { - image.Mutate(x => x - .BackgroundColor(Rgba32.Blue) - .Draw(Pens.Dash(Rgba32.HotPink, 5), simplePath.Clip(hole1))); + image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); + image.Mutate(x => x.Draw(Pens.Dash(Rgba32.HotPink, 5), simplePath.Clip(hole1))); image.Save($"{path}/Dashed.png"); } } @@ -171,7 +173,8 @@ namespace SixLabors.ImageSharp.Tests.Drawing using (var image = new Image(500, 500)) { - image.Mutate(x => x.BackgroundColor(Rgba32.Blue).Draw(color, 5, simplePath.Clip(hole1))); + image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); + image.Mutate(x => x.Draw(color, 5, simplePath.Clip(hole1))); image.Save($"{path}/Opacity.png"); //shift background color towards forground color by the opacity amount diff --git a/tests/ImageSharp.Tests/Drawing/LineTests.cs b/tests/ImageSharp.Tests/Drawing/LineTests.cs index 43dec547eb..1cc0fd1a40 100644 --- a/tests/ImageSharp.Tests/Drawing/LineTests.cs +++ b/tests/ImageSharp.Tests/Drawing/LineTests.cs @@ -19,8 +19,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing string path = TestEnvironment.CreateOutputDirectory("Drawing", "Lines"); using (var image = new Image(500, 500)) { + image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); image.Mutate( - x => x.BackgroundColor(Rgba32.Blue).DrawLines( + x => x.DrawLines( Rgba32.HotPink, 5, new Vector2(10, 10), @@ -43,8 +44,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing string path = TestEnvironment.CreateOutputDirectory("Drawing", "Lines"); using (var image = new Image(500, 500)) { + image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); image.Mutate( - x => x.BackgroundColor(Rgba32.Blue).DrawLines( + x => x.DrawLines( new GraphicsOptions(false), Rgba32.HotPink, 5, @@ -68,9 +70,8 @@ namespace SixLabors.ImageSharp.Tests.Drawing string path = TestEnvironment.CreateOutputDirectory("Drawing", "Lines"); using (var image = new Image(500, 500)) { - image.Mutate(x => x - .BackgroundColor(Rgba32.Blue) - .DrawLines(Pens.Dash(Rgba32.HotPink, 5), + image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); + image.Mutate(x => x.DrawLines(Pens.Dash(Rgba32.HotPink, 5), new SixLabors.Primitives.PointF[] { new Vector2(10, 10), new Vector2(200, 150), @@ -86,9 +87,8 @@ namespace SixLabors.ImageSharp.Tests.Drawing string path = TestEnvironment.CreateOutputDirectory("Drawing", "Lines"); using (var image = new Image(500, 500)) { - image.Mutate(x => x - .BackgroundColor(Rgba32.Blue) - .DrawLines(Pens.Dot(Rgba32.HotPink, 5), + image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); + image.Mutate(x => x.DrawLines(Pens.Dot(Rgba32.HotPink, 5), new SixLabors.Primitives.PointF[] { new Vector2(10, 10), new Vector2(200, 150), @@ -104,9 +104,8 @@ namespace SixLabors.ImageSharp.Tests.Drawing string path = TestEnvironment.CreateOutputDirectory("Drawing", "Lines"); using (var image = new Image(500, 500)) { - image.Mutate(x => x - .BackgroundColor(Rgba32.Blue) - .DrawLines(Pens.DashDot(Rgba32.HotPink, 5), + image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); + image.Mutate(x => x.DrawLines(Pens.DashDot(Rgba32.HotPink, 5), new SixLabors.Primitives.PointF[] { new Vector2(10, 10), new Vector2(200, 150), @@ -122,9 +121,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing string path = TestEnvironment.CreateOutputDirectory("Drawing", "Lines"); var image = new Image(500, 500); - image.Mutate(x => x - .BackgroundColor(Rgba32.Blue) - .DrawLines(Pens.DashDotDot(Rgba32.HotPink, 5), new SixLabors.Primitives.PointF[] { + image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); + image.Mutate(x => x.DrawLines(Pens.DashDotDot(Rgba32.HotPink, 5), + new SixLabors.Primitives.PointF[] { new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300) @@ -141,8 +140,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing var image = new Image(500, 500); + image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); image.Mutate( - x => x.BackgroundColor(Rgba32.Blue).DrawLines( + x => x.DrawLines( color, 10, new Vector2(10, 10), @@ -169,8 +169,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing var image = new Image(500, 500); + image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); image.Mutate( - x => x.BackgroundColor(Rgba32.Blue).DrawLines( + x => x.DrawLines( Rgba32.HotPink, 10, new Vector2(10, 10), diff --git a/tests/ImageSharp.Tests/Drawing/PolygonTests.cs b/tests/ImageSharp.Tests/Drawing/PolygonTests.cs index 6ea9c647f2..febb39c20d 100644 --- a/tests/ImageSharp.Tests/Drawing/PolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/PolygonTests.cs @@ -20,8 +20,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing using (Image image = new Image(500, 500)) { + image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); image.Mutate( - x => x.BackgroundColor(Rgba32.Blue).DrawPolygon( + x => x.DrawPolygon( Rgba32.HotPink, 5, new Vector2(10, 10), @@ -54,7 +55,8 @@ namespace SixLabors.ImageSharp.Tests.Drawing using (Image image = new Image(500, 500)) { - image.Mutate(x => x.BackgroundColor(Rgba32.Blue).DrawPolygon(color, 10, simplePath)); + image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); + image.Mutate(x => x.DrawPolygon(color, 10, simplePath)); image.Save($"{path}/Opacity.png"); //shift background color towards forground color by the opacity amount @@ -79,8 +81,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing using (Image image = new Image(500, 500)) { + image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); image.Mutate( - x => x.BackgroundColor(Rgba32.Blue).Draw(Rgba32.HotPink, 10, new Rectangle(10, 10, 190, 140))); + x => x.Draw(Rgba32.HotPink, 10, new Rectangle(10, 10, 190, 140))); image.Save($"{path}/Rectangle.png"); Buffer2D sourcePixels = image.GetRootFramePixelBuffer(); diff --git a/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs b/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs index 23acc1a44f..e8f53de010 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs @@ -24,15 +24,13 @@ namespace SixLabors.ImageSharp.Tests.Drawing new Vector2(300, 400) }; - TPixel blue = NamedColors.Blue; - TPixel hotPink = NamedColors.HotPink; + Color blue = Color.Blue; + Color hotPink = Color.HotPink; using (Image image = provider.GetImage()) { - - image.Mutate(x => x - .BackgroundColor(blue) - .Fill(hotPink, new Polygon(new CubicBezierLineSegment(simplePath)))); + image.Mutate(x => x.BackgroundColor(blue)); + image.Mutate(x => x.Fill(hotPink.ToPixel(), new Polygon(new CubicBezierLineSegment(simplePath)))); image.DebugSave(provider); image.CompareToReferenceOutput(provider); } @@ -55,9 +53,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing using (var image = provider.GetImage() as Image) { - image.Mutate(x => x - .BackgroundColor(Rgba32.Blue) - .Fill(color, new Polygon(new CubicBezierLineSegment(simplePath)))); + image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); + + image.Mutate(x => x.Fill(color, new Polygon(new CubicBezierLineSegment(simplePath)))); image.DebugSave(provider); image.CompareToReferenceOutput(provider); } diff --git a/tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs index 2c9628e842..0e997ad7eb 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs @@ -31,7 +31,8 @@ namespace SixLabors.ImageSharp.Tests.Drawing // var clipped = new Rectangle(10, 10, 100, 100).Clip(new Rectangle(20, 0, 20, 20)); using (var image = new Image(500, 500)) { - image.Mutate(x => x.BackgroundColor(Rgba32.Blue).Fill(Rgba32.HotPink, clipped)); + image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); + image.Mutate(x => x.Fill(Rgba32.HotPink, clipped)); image.Save($"{path}/Simple.png"); Buffer2D sourcePixels = image.GetRootFramePixelBuffer(); @@ -59,7 +60,8 @@ namespace SixLabors.ImageSharp.Tests.Drawing using (var image = new Image(500, 500)) { - image.Mutate(x => x.BackgroundColor(Rgba32.Blue).Fill(Rgba32.HotPink, simplePath.Clip(hole1))); + image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); + image.Mutate(x => x.Fill(Rgba32.HotPink, simplePath.Clip(hole1))); image.Save($"{path}/SimpleOverlapping.png"); Buffer2D sourcePixels = image.GetRootFramePixelBuffer(); @@ -88,7 +90,8 @@ namespace SixLabors.ImageSharp.Tests.Drawing using (var image = new Image(500, 500)) { - image.Mutate(x => x.BackgroundColor(Rgba32.Blue).Fill(color, simplePath.Clip(hole1))); + image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); + image.Mutate(x => x.Fill(color, simplePath.Clip(hole1))); image.Save($"{path}/Opacity.png"); //shift background color towards forground color by the opacity amount diff --git a/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs index 5660518eb4..6a299aad7b 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs @@ -68,8 +68,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing using (var image = new Image(500, 500)) { + image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); image.Mutate( - x => x.BackgroundColor(Rgba32.Blue).FillPolygon( + x => x.FillPolygon( new GraphicsOptions(false), Rgba32.HotPink, simplePath)); @@ -101,9 +102,8 @@ namespace SixLabors.ImageSharp.Tests.Drawing { var brush = new ImageBrush(brushImage); - image.Mutate(x => x - .BackgroundColor(Rgba32.Blue) - .FillPolygon(brush, simplePath)); + image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); + image.Mutate(x => x.FillPolygon(brush, simplePath)); image.Save($"{path}/Image.png"); } } @@ -121,7 +121,8 @@ namespace SixLabors.ImageSharp.Tests.Drawing using (var image = new Image(500, 500)) { - image.Mutate(x => x.BackgroundColor(Rgba32.Blue).FillPolygon(color, simplePath)); + image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); + image.Mutate(x => x.FillPolygon(color, simplePath)); image.Save($"{path}/Opacity.png"); //shift background color towards forground color by the opacity amount @@ -140,8 +141,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing using (var image = new Image(500, 500)) { + image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); image.Mutate( - x => x.BackgroundColor(Rgba32.Blue).Fill( + x => x.Fill( Rgba32.HotPink, new SixLabors.Shapes.RectangularPolygon(10, 10, 190, 140))); image.Save($"{path}/Rectangle.png"); @@ -166,8 +168,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing using (var image = new Image(100, 100)) { + image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); image.Mutate( - x => x.BackgroundColor(Rgba32.Blue).Fill(Rgba32.HotPink, new RegularPolygon(50, 50, 3, 30))); + x => x.Fill(Rgba32.HotPink, new RegularPolygon(50, 50, 3, 30))); image.Save($"{path}/Triangle.png"); Buffer2D sourcePixels = image.GetRootFramePixelBuffer(); @@ -186,9 +189,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing config.MaxDegreeOfParallelism = 1; using (var image = new Image(config, 100, 100)) { - image.Mutate(x => x - .BackgroundColor(Rgba32.Blue) - .Fill(Rgba32.HotPink, new RegularPolygon(50, 50, 7, 30, -(float)Math.PI))); + image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); + image.Mutate(x => x.Fill(Rgba32.HotPink, + new RegularPolygon(50, 50, 7, 30, -(float)Math.PI))); image.Save($"{path}/Septagon.png"); } } @@ -202,8 +205,8 @@ namespace SixLabors.ImageSharp.Tests.Drawing config.MaxDegreeOfParallelism = 1; using (var image = new Image(config, 100, 100)) { + image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); image.Mutate(x => x - .BackgroundColor(Rgba32.Blue) .Fill(Rgba32.HotPink, new EllipsePolygon(50, 50, 30, 50) .Rotate((float)(Math.PI / 3)))); image.Save($"{path}/ellipse.png"); diff --git a/tests/ImageSharp.Tests/Processing/Effects/BackgroundColorTest.cs b/tests/ImageSharp.Tests/Processing/Effects/BackgroundColorTest.cs index 7775de2d24..14758958f0 100644 --- a/tests/ImageSharp.Tests/Processing/Effects/BackgroundColorTest.cs +++ b/tests/ImageSharp.Tests/Processing/Effects/BackgroundColorTest.cs @@ -14,41 +14,41 @@ namespace SixLabors.ImageSharp.Tests.Processing.Effects [Fact] public void BackgroundColor_amount_BackgroundColorProcessorDefaultsSet() { - this.operations.BackgroundColor(Rgba32.BlanchedAlmond); - var processor = this.Verify>(); + this.operations.BackgroundColor(Color.BlanchedAlmond); + var processor = this.Verify(); Assert.Equal(GraphicsOptions.Default, processor.GraphicsOptions); - Assert.Equal(Rgba32.BlanchedAlmond, processor.Color); + Assert.Equal(Color.BlanchedAlmond, processor.Color); } [Fact] public void BackgroundColor_amount_rect_BackgroundColorProcessorDefaultsSet() { - this.operations.BackgroundColor(Rgba32.BlanchedAlmond, this.rect); - var processor = this.Verify>(this.rect); + this.operations.BackgroundColor(Color.BlanchedAlmond, this.rect); + var processor = this.Verify(this.rect); Assert.Equal(GraphicsOptions.Default, processor.GraphicsOptions); - Assert.Equal(Rgba32.BlanchedAlmond, processor.Color); + Assert.Equal(Color.BlanchedAlmond, processor.Color); } [Fact] public void BackgroundColor_amount_options_BackgroundColorProcessorDefaultsSet() { - this.operations.BackgroundColor(this.options, Rgba32.BlanchedAlmond); - var processor = this.Verify>(); + this.operations.BackgroundColor(this.options, Color.BlanchedAlmond); + var processor = this.Verify(); Assert.Equal(this.options, processor.GraphicsOptions); - Assert.Equal(Rgba32.BlanchedAlmond, processor.Color); + Assert.Equal(Color.BlanchedAlmond, processor.Color); } [Fact] public void BackgroundColor_amount_rect_options_BackgroundColorProcessorDefaultsSet() { - this.operations.BackgroundColor(this.options, Rgba32.BlanchedAlmond, this.rect); - var processor = this.Verify>(this.rect); + this.operations.BackgroundColor(this.options, Color.BlanchedAlmond, this.rect); + var processor = this.Verify(this.rect); Assert.Equal(this.options, processor.GraphicsOptions); - Assert.Equal(Rgba32.BlanchedAlmond, processor.Color); + Assert.Equal(Color.BlanchedAlmond, processor.Color); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs b/tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs index 899082e361..0301f5c039 100644 --- a/tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs +++ b/tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs @@ -18,10 +18,10 @@ namespace SixLabors.ImageSharp.Tests.Processing.Overlays public void Glow_GlowProcessorWithDefaultValues() { this.operations.Glow(); - var p = this.Verify>(); + var p = this.Verify(); Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); - Assert.Equal(Rgba32.Black, p.GlowColor); + Assert.Equal(Color.Black, p.GlowColor); Assert.Equal(ValueSize.PercentageOfWidth(.5f), p.Radius); } @@ -29,10 +29,10 @@ namespace SixLabors.ImageSharp.Tests.Processing.Overlays public void Glow_Color_GlowProcessorWithDefaultValues() { this.operations.Glow(Rgba32.Aquamarine); - var p = this.Verify>(); + var p = this.Verify(); Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); - Assert.Equal(Rgba32.Aquamarine, p.GlowColor); + Assert.Equal(Color.Aquamarine, p.GlowColor); Assert.Equal(ValueSize.PercentageOfWidth(.5f), p.Radius); } @@ -40,10 +40,10 @@ namespace SixLabors.ImageSharp.Tests.Processing.Overlays public void Glow_Radux_GlowProcessorWithDefaultValues() { this.operations.Glow(3.5f); - var p = this.Verify>(); + var p = this.Verify(); Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); - Assert.Equal(Rgba32.Black, p.GlowColor); + Assert.Equal(Color.Black, p.GlowColor); Assert.Equal(ValueSize.Absolute(3.5f), p.Radius); } @@ -52,10 +52,10 @@ namespace SixLabors.ImageSharp.Tests.Processing.Overlays { var rect = new Rectangle(12, 123, 43, 65); this.operations.Glow(rect); - var p = this.Verify>(rect); + var p = this.Verify(rect); Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); - Assert.Equal(Rgba32.Black, p.GlowColor); + Assert.Equal(Color.Black, p.GlowColor); Assert.Equal(ValueSize.PercentageOfWidth(.5f), p.Radius); } } diff --git a/tests/ImageSharp.Tests/Processing/Overlays/VignetteTest.cs b/tests/ImageSharp.Tests/Processing/Overlays/VignetteTest.cs index f47bffe26f..2538aa137c 100644 --- a/tests/ImageSharp.Tests/Processing/Overlays/VignetteTest.cs +++ b/tests/ImageSharp.Tests/Processing/Overlays/VignetteTest.cs @@ -16,10 +16,10 @@ namespace SixLabors.ImageSharp.Tests.Processing.Overlays public void Vignette_VignetteProcessorWithDefaultValues() { this.operations.Vignette(); - var p = this.Verify>(); + var p = this.Verify(); Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); - Assert.Equal(Rgba32.Black, p.VignetteColor); + Assert.Equal(Color.Black, p.VignetteColor); Assert.Equal(ValueSize.PercentageOfWidth(.5f), p.RadiusX); Assert.Equal(ValueSize.PercentageOfHeight(.5f), p.RadiusY); } @@ -27,11 +27,11 @@ namespace SixLabors.ImageSharp.Tests.Processing.Overlays [Fact] public void Vignette_Color_VignetteProcessorWithDefaultValues() { - this.operations.Vignette(Rgba32.Aquamarine); - var p = this.Verify>(); + this.operations.Vignette(Color.Aquamarine); + var p = this.Verify(); Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); - Assert.Equal(Rgba32.Aquamarine, p.VignetteColor); + Assert.Equal(Color.Aquamarine, p.VignetteColor); Assert.Equal(ValueSize.PercentageOfWidth(.5f), p.RadiusX); Assert.Equal(ValueSize.PercentageOfHeight(.5f), p.RadiusY); } @@ -40,10 +40,10 @@ namespace SixLabors.ImageSharp.Tests.Processing.Overlays public void Vignette_Radux_VignetteProcessorWithDefaultValues() { this.operations.Vignette(3.5f, 12123f); - var p = this.Verify>(); + var p = this.Verify(); Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); - Assert.Equal(Rgba32.Black, p.VignetteColor); + Assert.Equal(Color.Black, p.VignetteColor); Assert.Equal(ValueSize.Absolute(3.5f), p.RadiusX); Assert.Equal(ValueSize.Absolute(12123f), p.RadiusY); } @@ -53,10 +53,10 @@ namespace SixLabors.ImageSharp.Tests.Processing.Overlays { var rect = new Rectangle(12, 123, 43, 65); this.operations.Vignette(rect); - var p = this.Verify>(rect); + var p = this.Verify(rect); Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); - Assert.Equal(Rgba32.Black, p.VignetteColor); + Assert.Equal(Color.Black, p.VignetteColor); Assert.Equal(ValueSize.PercentageOfWidth(.5f), p.RadiusX); Assert.Equal(ValueSize.PercentageOfHeight(.5f), p.RadiusY); } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Effects/BackgroundColorTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Effects/BackgroundColorTest.cs index d7f77c9565..dc6ff6f3eb 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Effects/BackgroundColorTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Effects/BackgroundColorTest.cs @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects public void FullImage(TestImageProvider provider) where TPixel : struct, IPixel { - provider.RunValidatingProcessorTest(x => x.BackgroundColor(NamedColors.HotPink)); + provider.RunValidatingProcessorTest(x => x.BackgroundColor(Color.HotPink)); } [Theory] @@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects where TPixel : struct, IPixel { provider.RunRectangleConstrainedValidatingProcessorTest( - (x, rect) => x.BackgroundColor(NamedColors.HotPink, rect)); + (x, rect) => x.BackgroundColor(Color.HotPink, rect)); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Overlays/GlowTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Overlays/GlowTest.cs index 113c13b8ae..27aeb4b053 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Overlays/GlowTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Overlays/GlowTest.cs @@ -13,11 +13,11 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Overlays [GroupOutput("Overlays")] public class GlowTest : OverlayTestBase { - protected override void Apply(IImageProcessingContext ctx, T color) => ctx.Glow(color); + protected override void Apply(IImageProcessingContext ctx, Color color) => ctx.Glow(color); - protected override void Apply(IImageProcessingContext ctx, float radiusX, float radiusY) => + protected override void Apply(IImageProcessingContext ctx, float radiusX, float radiusY) => ctx.Glow(radiusX); - protected override void Apply(IImageProcessingContext ctx, Rectangle rect) => ctx.Glow(rect); + protected override void Apply(IImageProcessingContext ctx, Rectangle rect) => ctx.Glow(rect); } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Overlays/OverlayTestBase.cs b/tests/ImageSharp.Tests/Processing/Processors/Overlays/OverlayTestBase.cs index d2abcd7312..788f7e1ade 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Overlays/OverlayTestBase.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Overlays/OverlayTestBase.cs @@ -27,8 +27,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Overlays where TPixel : struct, IPixel { provider.Utility.TestGroupName = this.GetType().Name; - var f = (FieldInfo)typeof(NamedColors).GetMember(colorName)[0]; - TPixel color = (TPixel)f.GetValue(null); + var f = (FieldInfo)typeof(Color).GetMember(colorName)[0]; + Color color = (Color)f.GetValue(null); provider.RunValidatingProcessorTest(x => this.Apply(x, color), colorName, ValidatorComparer, appendPixelTypeToFileName: false); } @@ -58,13 +58,10 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Overlays provider.RunRectangleConstrainedValidatingProcessorTest((x, rect) => this.Apply(x, rect)); } - protected abstract void Apply(IImageProcessingContext ctx, T color) - where T : struct, IPixel; + protected abstract void Apply(IImageProcessingContext ctx, Color color); - protected abstract void Apply(IImageProcessingContext ctx, float radiusX, float radiusY) - where T : struct, IPixel; + protected abstract void Apply(IImageProcessingContext ctx, float radiusX, float radiusY); - protected abstract void Apply(IImageProcessingContext ctx, Rectangle rect) - where T : struct, IPixel; + protected abstract void Apply(IImageProcessingContext ctx, Rectangle rect); } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Overlays/VignetteTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Overlays/VignetteTest.cs index ad04a827db..9448feefe2 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Overlays/VignetteTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Overlays/VignetteTest.cs @@ -9,11 +9,11 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Overlays [GroupOutput("Overlays")] public class VignetteTest : OverlayTestBase { - protected override void Apply(IImageProcessingContext ctx, T color) => ctx.Vignette(color); + protected override void Apply(IImageProcessingContext ctx, Color color) => ctx.Vignette(color); - protected override void Apply(IImageProcessingContext ctx, float radiusX, float radiusY) => + protected override void Apply(IImageProcessingContext ctx, float radiusX, float radiusY) => ctx.Vignette(radiusX, radiusY); - protected override void Apply(IImageProcessingContext ctx, Rectangle rect) => ctx.Vignette(rect); + protected override void Apply(IImageProcessingContext ctx, Rectangle rect) => ctx.Vignette(rect); } } \ No newline at end of file From 139a0000ce5efedc2a3d2a1080f49cc64976f754 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 9 May 2019 23:49:58 +0200 Subject: [PATCH 154/223] made AutoOrientExtensions non-generic --- src/ImageSharp/Processing/AutoOrientExtensions.cs | 10 ++++------ .../Processing/Transforms/AutoOrientTests.cs | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/ImageSharp/Processing/AutoOrientExtensions.cs b/src/ImageSharp/Processing/AutoOrientExtensions.cs index d065d4583d..a831e2d9af 100644 --- a/src/ImageSharp/Processing/AutoOrientExtensions.cs +++ b/src/ImageSharp/Processing/AutoOrientExtensions.cs @@ -7,7 +7,7 @@ using SixLabors.ImageSharp.Processing.Processors.Transforms; namespace SixLabors.ImageSharp.Processing { /// - /// Defines extensions that allow the application of auto-orientation operations to an + /// Defines extensions that allow the application of auto-orientation operations to an /// using Mutate/Clone. /// public static class AutoOrientExtensions @@ -15,11 +15,9 @@ namespace SixLabors.ImageSharp.Processing /// /// Adjusts an image so that its orientation is suitable for viewing. Adjustments are based on EXIF metadata embedded in the image. /// - /// The pixel format. /// The image to auto rotate. - /// The - public static IImageProcessingContext AutoOrient(this IImageProcessingContext source) - where TPixel : struct, IPixel - => source.ApplyProcessor(new AutoOrientProcessor()); + /// The to allow chaining of operations. + public static IImageProcessingContext AutoOrient(this IImageProcessingContext source) + => source.ApplyProcessor(new AutoOrientProcessor()); } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Transforms/AutoOrientTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/AutoOrientTests.cs index bba4661db0..0547b46e3b 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/AutoOrientTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/AutoOrientTests.cs @@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms public void AutoOrient_AutoOrientProcessor() { this.operations.AutoOrient(); - this.Verify>(); + this.Verify(); } } } \ No newline at end of file From ea8f7b748fb04febb3d8d3791b5a93d1799e375a Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 10 May 2019 00:24:28 +0200 Subject: [PATCH 155/223] cleanup and document Color --- src/ImageSharp/Color/Color.Conversions.cs | 162 +++++++++++++++++++ src/ImageSharp/Color/Color.NamedColors.cs | 7 +- src/ImageSharp/Color/Color.WebSafePalette.cs | 9 +- src/ImageSharp/Color/Color.WernerPalette.cs | 7 +- src/ImageSharp/Color/Color.cs | 107 ++++++------ 5 files changed, 226 insertions(+), 66 deletions(-) create mode 100644 src/ImageSharp/Color/Color.Conversions.cs diff --git a/src/ImageSharp/Color/Color.Conversions.cs b/src/ImageSharp/Color/Color.Conversions.cs new file mode 100644 index 0000000000..001aee5a4f --- /dev/null +++ b/src/ImageSharp/Color/Color.Conversions.cs @@ -0,0 +1,162 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp +{ + /// + /// Contains constructors and implicit conversion methods. + /// + public readonly partial struct Color + { + /// + /// Initializes a new instance of the struct. + /// + /// The containing the color information. + public Color(Rgba64 pixel) + { + this.data = pixel; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The containing the color information. + public Color(Rgba32 pixel) + { + this.data = new Rgba64(pixel); + } + + /// + /// Initializes a new instance of the struct. + /// + /// The containing the color information. + public Color(Argb32 pixel) + { + this.data = new Rgba64(pixel); + } + + /// + /// Initializes a new instance of the struct. + /// + /// The containing the color information. + public Color(Bgra32 pixel) + { + this.data = new Rgba64(pixel); + } + + /// + /// Initializes a new instance of the struct. + /// + /// The containing the color information. + public Color(Rgb24 pixel) + { + this.data = new Rgba64(pixel); + } + + /// + /// Initializes a new instance of the struct. + /// + /// The containing the color information. + public Color(Bgr24 pixel) + { + this.data = new Rgba64(pixel); + } + + /// + /// Initializes a new instance of the struct. + /// + /// The containing the color information. + public Color(Vector4 vector) + { + this.data = new Rgba64(vector); + } + + /// + /// Converts an to . + /// + /// The . + /// The . + public static implicit operator Color(Rgba64 source) => new Color(source); + + /// + /// Converts an to . + /// + /// The . + /// The . + public static implicit operator Color(Rgba32 source) => new Color(source); + + /// + /// Converts an to . + /// + /// The . + /// The . + public static implicit operator Color(Bgra32 source) => new Color(source); + + /// + /// Converts an to . + /// + /// The . + /// The . + public static implicit operator Color(Argb32 source) => new Color(source); + + /// + /// Converts an to . + /// + /// The . + /// The . + public static implicit operator Color(Rgb24 source) => new Color(source); + + /// + /// Converts an to . + /// + /// The . + /// The . + public static implicit operator Color(Bgr24 source) => new Color(source); + + /// + /// Converts a to . + /// + /// The . + /// The . + public static implicit operator Rgba64(Color color) => color.data; + + /// + /// Converts a to . + /// + /// The . + /// The . + public static implicit operator Rgba32(Color color) => color.data.ToRgba32(); + + /// + /// Converts a to . + /// + /// The . + /// The . + public static implicit operator Bgra32(Color color) => color.data.ToBgra32(); + + /// + /// Converts a to . + /// + /// The . + /// The . + public static implicit operator Argb32(Color color) => color.data.ToArgb32(); + + /// + /// Converts a to . + /// + /// The . + /// The . + public static implicit operator Rgb24(Color color) => color.data.ToRgb24(); + + /// + /// Converts a to . + /// + /// The . + /// The . + public static implicit operator Bgr24(Color color) => color.data.ToBgr24(); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Color/Color.NamedColors.cs b/src/ImageSharp/Color/Color.NamedColors.cs index a712f85dab..8811516c1c 100644 --- a/src/ImageSharp/Color/Color.NamedColors.cs +++ b/src/ImageSharp/Color/Color.NamedColors.cs @@ -1,8 +1,11 @@ -// // Copyright (c) Six Labors and contributors. -// // Licensed under the Apache License, Version 2.0. +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp { + /// + /// Contains static named color values. + /// public readonly partial struct Color { /// diff --git a/src/ImageSharp/Color/Color.WebSafePalette.cs b/src/ImageSharp/Color/Color.WebSafePalette.cs index 13720ec493..506432ac2d 100644 --- a/src/ImageSharp/Color/Color.WebSafePalette.cs +++ b/src/ImageSharp/Color/Color.WebSafePalette.cs @@ -1,16 +1,19 @@ -// // Copyright (c) Six Labors and contributors. -// // Licensed under the Apache License, Version 2.0. +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. using System; namespace SixLabors.ImageSharp { + /// + /// Contains the definition of . + /// public partial struct Color { private static readonly Lazy WebSafePaletteLazy = new Lazy(CreateWebSafePalette, true); /// - /// Gets a collection of named, web safe, colors as defined in the CSS Color Module Level 4. + /// Gets a collection of named, web safe colors as defined in the CSS Color Module Level 4. /// public static ReadOnlySpan WebSafePalette => WebSafePaletteLazy.Value; diff --git a/src/ImageSharp/Color/Color.WernerPalette.cs b/src/ImageSharp/Color/Color.WernerPalette.cs index 45823479e1..37980b15fd 100644 --- a/src/ImageSharp/Color/Color.WernerPalette.cs +++ b/src/ImageSharp/Color/Color.WernerPalette.cs @@ -1,10 +1,13 @@ -// // Copyright (c) Six Labors and contributors. -// // Licensed under the Apache License, Version 2.0. +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. using System; namespace SixLabors.ImageSharp { + /// + /// Contains the definition of . + /// public partial struct Color { private static readonly Lazy WernerPaletteLazy = new Lazy(CreateWernerPalette, true); diff --git a/src/ImageSharp/Color/Color.cs b/src/ImageSharp/Color/Color.cs index 021cea4159..94b5dde2ee 100644 --- a/src/ImageSharp/Color/Color.cs +++ b/src/ImageSharp/Color/Color.cs @@ -11,81 +11,58 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp { + /// + /// Represents a color value that is convertible to all implementations. + /// public readonly partial struct Color : IEquatable { private readonly Rgba64 data; - public Color(Rgba64 pixel) - { - this.data = pixel; - } - - public Color(Rgba32 pixel) - { - this.data = new Rgba64(pixel); - } - - public Color(Argb32 pixel) - { - this.data = new Rgba64(pixel); - } - - public Color(Bgra32 pixel) - { - this.data = new Rgba64(pixel); - } - - public Color(Rgb24 pixel) - { - this.data = new Rgba64(pixel); - } - - public Color(Bgr24 pixel) - { - this.data = new Rgba64(pixel); - } - - public Color(Vector4 vector) - { - this.data = new Rgba64(vector); - } - - public static implicit operator Color(Rgba64 source) => new Color(source); - - public static implicit operator Color(Rgba32 source) => new Color(source); - - public static implicit operator Color(Bgra32 source) => new Color(source); - - public static implicit operator Color(Argb32 source) => new Color(source); - - public static implicit operator Color(Rgb24 source) => new Color(source); - - public static implicit operator Color(Bgr24 source) => new Color(source); - - public static implicit operator Rgba64(Color color) => color.data; - - public static implicit operator Rgba32(Color color) => color.data.ToRgba32(); - - public static implicit operator Bgra32(Color color) => color.data.ToBgra32(); - - public static implicit operator Argb32(Color color) => color.data.ToArgb32(); - - public static implicit operator Rgb24(Color color) => color.data.ToRgb24(); - - public static implicit operator Bgr24(Color color) => color.data.ToBgr24(); - + /// + /// Checks whether two structures are equal. + /// + /// The left hand operand. + /// The right hand operand. + /// + /// True if the parameter is equal to the parameter; + /// otherwise, false. + /// public static bool operator ==(Color left, Color right) { return left.Equals(right); } + /// + /// Checks whether two structures are equal. + /// + /// The left hand operand. + /// The right hand operand. + /// + /// True if the parameter is not equal to the parameter; + /// otherwise, false. + /// public static bool operator !=(Color left, Color right) { return !left.Equals(right); } + /// + /// Creates a from RGBA bytes. + /// + /// The red component (0-255). + /// The green component (0-255). + /// The blue component (0-255). + /// The alpha component (0-255). + /// The . public static Color FromRgba(byte r, byte g, byte b, byte a) => new Color(new Rgba32(r, g, b, a)); + /// + /// Creates a from RGB bytes. + /// + /// The red component (0-255). + /// The green component (0-255). + /// The blue component (0-255). + /// The . public static Color FromRgb(byte r, byte g, byte b) => FromRgba(r, g, b, 255); /// @@ -120,6 +97,12 @@ namespace SixLabors.ImageSharp /// public override string ToString() => this.ToHex(); + /// + /// Converts the color instance to an + /// implementation defined by . + /// + /// The pixel type to convert to. + /// The pixel value. public TPixel ToPixel() where TPixel : struct, IPixel { @@ -128,17 +111,20 @@ namespace SixLabors.ImageSharp return pixel; } + /// public bool Equals(Color other) { return this.data.PackedValue == other.data.PackedValue; } + /// public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) { return false; } + return obj is Color other && this.Equals(other); } @@ -148,6 +134,9 @@ namespace SixLabors.ImageSharp return this.data.PackedValue.GetHashCode(); } + /// + /// Bulk convert a span of to a span of a specified pixel type. + /// internal static void ToPixel( Configuration configuration, ReadOnlySpan source, From 2cd94fa1f817ad5a88828b7d75b4c0b5d86806a4 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 10 May 2019 00:26:34 +0200 Subject: [PATCH 156/223] cleanup --- .../Processors/Binarization/BinaryOrderedDitherProcessor.cs | 3 +++ .../Processors/Binarization/BinaryThresholdProcessor.cs | 4 ++-- .../Processors/Overlays/BackgroundColorProcessor.cs | 2 +- .../Processing/Processors/Overlays/GlowProcessor.cs | 1 - 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor.cs index 99c57171cf..6cf1a95266 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor.cs @@ -8,6 +8,9 @@ using SixLabors.ImageSharp.Processing.Processors.Dithering; namespace SixLabors.ImageSharp.Processing.Processors.Binarization { + /// + /// Defines a binary threshold filtering using ordered dithering. + /// public class BinaryOrderedDitherProcessor : IImageProcessor { /// diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs index 50086936cd..83701aa8a2 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs @@ -41,12 +41,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization public float Threshold { get; } /// - /// Gets or sets the color to use for pixels that are above the threshold. + /// Gets the color to use for pixels that are above the threshold. /// public Color UpperColor { get; } /// - /// Gets or sets the color to use for pixels that fall below the threshold. + /// Gets the color to use for pixels that fall below the threshold. /// public Color LowerColor { get; } diff --git a/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor.cs index 0dd0c41188..62848172a1 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor.cs @@ -6,7 +6,7 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Processing.Processors.Overlays { /// - /// Defines a processing operation to replace the background color of an . + /// Defines a processing operation to replace the background color of an . /// public sealed class BackgroundColorProcessor : IImageProcessor { diff --git a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs index 4927a0ed7a..dcdddb217c 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs @@ -20,7 +20,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays { } - /// /// Initializes a new instance of the class. /// From 6a9123b09c016ed2d33648963ba880ccada550f1 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 10 May 2019 20:28:46 +0200 Subject: [PATCH 157/223] Switched back to the default reference decoder --- tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs index a56cee47d8..f3d6e0fa8e 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs @@ -153,13 +153,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp [WithFile(Bit8Palette4, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel8)] [WithFile(Bit8Gs, PixelTypes.Gray8, BmpBitsPerPixel.Pixel8)] public void Encode_8Bit_WithV3Header_Works(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) - where TPixel : struct, IPixel => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: false, referenceDecoder: new MagickReferenceDecoder()); + where TPixel : struct, IPixel => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: false); [Theory] [WithFile(Bit8Palette4, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel8)] [WithFile(Bit8Gs, PixelTypes.Gray8, BmpBitsPerPixel.Pixel8)] public void Encode_8Bit_WithV4Header_Works(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) - where TPixel : struct, IPixel => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: true, referenceDecoder: new MagickReferenceDecoder()); + where TPixel : struct, IPixel => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: true); [Theory] [WithFile(TestImages.Png.GrayAlpha2BitInterlaced, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel32)] @@ -167,7 +167,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp public void Encode_PreservesAlpha(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) where TPixel : struct, IPixel => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: true); - private static void TestBmpEncoderCore(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel, bool supportTransparency = true, IImageDecoder referenceDecoder = null) + private static void TestBmpEncoderCore(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel, bool supportTransparency = true) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) @@ -181,7 +181,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp var encoder = new BmpEncoder { BitsPerPixel = bitsPerPixel, SupportTransparency = supportTransparency }; // Does DebugSave & load reference CompareToReferenceInput(): - image.VerifyEncoder(provider, "bmp", bitsPerPixel, encoder, referenceDecoder: referenceDecoder); + image.VerifyEncoder(provider, "bmp", bitsPerPixel, encoder); } } } From 5d470807d8aad1ebb06c4c712389354b94f74eca Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 10 May 2019 22:59:43 +0200 Subject: [PATCH 158/223] Made PaletteQuantizer non-generic all the way --- src/ImageSharp/Color/Color.WebSafePalette.cs | 2 +- src/ImageSharp/Color/Color.WernerPalette.cs | 2 +- src/ImageSharp/Formats/Gif/GifEncoderCore.cs | 11 +- .../Processing/DiffuseExtensions.cs | 4 +- src/ImageSharp/Processing/DitherExtensions.cs | 4 +- .../ErrorDiffusionPaletteProcessor.cs | 2 +- .../OrderedDitherPaletteProcessor.cs | 2 +- .../Dithering/PaletteDitherProcessor.cs | 10 +- .../PaletteDitherProcessor{TPixel}.cs | 51 ++++---- .../Processors/Overlays/GlowProcessor.cs | 2 +- ...e{TPixel}.cs => FrameQuantizer{TPixel}.cs} | 43 +++++-- .../OctreeFrameQuantizer{TPixel}.cs | 6 +- .../PaletteFrameQuantizer{TPixel}.cs | 13 ++- .../Quantization/PaletteQuantizer.cs | 59 +++++----- .../Quantization/PaletteQuantizer{TPixel}.cs | 110 ------------------ .../Quantization/WebSafePaletteQuantizer.cs | 13 +-- .../Quantization/WernerPaletteQuantizer.cs | 13 +-- .../Quantization/WuFrameQuantizer{TPixel}.cs | 6 +- .../Processing/Dithering/DitherTest.cs | 2 +- .../Quantization/PaletteQuantizerTests.cs | 28 ++--- 20 files changed, 143 insertions(+), 240 deletions(-) rename src/ImageSharp/Processing/Processors/Quantization/{FrameQuantizerBase{TPixel}.cs => FrameQuantizer{TPixel}.cs} (81%) delete mode 100644 src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer{TPixel}.cs diff --git a/src/ImageSharp/Color/Color.WebSafePalette.cs b/src/ImageSharp/Color/Color.WebSafePalette.cs index 506432ac2d..8e5fb2a55b 100644 --- a/src/ImageSharp/Color/Color.WebSafePalette.cs +++ b/src/ImageSharp/Color/Color.WebSafePalette.cs @@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp /// /// Gets a collection of named, web safe colors as defined in the CSS Color Module Level 4. /// - public static ReadOnlySpan WebSafePalette => WebSafePaletteLazy.Value; + public static ReadOnlyMemory WebSafePalette => WebSafePaletteLazy.Value; private static Color[] CreateWebSafePalette() => new[] { diff --git a/src/ImageSharp/Color/Color.WernerPalette.cs b/src/ImageSharp/Color/Color.WernerPalette.cs index 37980b15fd..768fe065cd 100644 --- a/src/ImageSharp/Color/Color.WernerPalette.cs +++ b/src/ImageSharp/Color/Color.WernerPalette.cs @@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp /// Gets a collection of colors as defined in the original second edition of Werner’s Nomenclature of Colours 1821. /// The hex codes were collected and defined by Nicholas Rougeux . /// - public static ReadOnlySpan WernerPalette => WernerPaletteLazy.Value; + public static ReadOnlyMemory WernerPalette => WernerPaletteLazy.Value; private static Color[] CreateWernerPalette() => new[] { diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs index 12a515cca7..2770790a28 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs @@ -144,8 +144,6 @@ namespace SixLabors.ImageSharp.Formats.Gif private void EncodeGlobal(Image image, QuantizedFrame quantized, int transparencyIndex, Stream stream) where TPixel : struct, IPixel { - var palleteQuantizer = new PaletteQuantizer(quantized.Palette, this.quantizer.Diffuser); - for (int i = 0; i < image.Frames.Count; i++) { ImageFrame frame = image.Frames[i]; @@ -160,10 +158,13 @@ namespace SixLabors.ImageSharp.Formats.Gif } else { - using (IFrameQuantizer palleteFrameQuantizer = palleteQuantizer.CreateFrameQuantizer(image.GetConfiguration())) - using (QuantizedFrame paletteQuantized = palleteFrameQuantizer.QuantizeFrame(frame)) + using (IFrameQuantizer palleteFrameQuantizer = + new PaletteFrameQuantizer(this.quantizer.Diffuser, quantized.Palette)) { - this.WriteImageData(paletteQuantized, stream); + using (QuantizedFrame paletteQuantized = palleteFrameQuantizer.QuantizeFrame(frame)) + { + this.WriteImageData(paletteQuantized, stream); + } } } } diff --git a/src/ImageSharp/Processing/DiffuseExtensions.cs b/src/ImageSharp/Processing/DiffuseExtensions.cs index cd714c3da3..f9a1bdde0e 100644 --- a/src/ImageSharp/Processing/DiffuseExtensions.cs +++ b/src/ImageSharp/Processing/DiffuseExtensions.cs @@ -73,7 +73,7 @@ namespace SixLabors.ImageSharp.Processing.Dithering this IImageProcessingContext source, IErrorDiffuser diffuser, float threshold, - ReadOnlySpan palette) => + ReadOnlyMemory palette) => source.ApplyProcessor(new ErrorDiffusionPaletteProcessor(diffuser, threshold, palette)); /// @@ -91,7 +91,7 @@ namespace SixLabors.ImageSharp.Processing.Dithering this IImageProcessingContext source, IErrorDiffuser diffuser, float threshold, - ReadOnlySpan palette, + ReadOnlyMemory palette, Rectangle rectangle) => source.ApplyProcessor(new ErrorDiffusionPaletteProcessor(diffuser, threshold, palette), rectangle); } diff --git a/src/ImageSharp/Processing/DitherExtensions.cs b/src/ImageSharp/Processing/DitherExtensions.cs index 55794aec20..f83a9e9e81 100644 --- a/src/ImageSharp/Processing/DitherExtensions.cs +++ b/src/ImageSharp/Processing/DitherExtensions.cs @@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Processing public static IImageProcessingContext Dither( this IImageProcessingContext source, IOrderedDither dither, - ReadOnlySpan palette) => + ReadOnlyMemory palette) => source.ApplyProcessor(new OrderedDitherPaletteProcessor(dither, palette)); /// @@ -72,7 +72,7 @@ namespace SixLabors.ImageSharp.Processing public static IImageProcessingContext Dither( this IImageProcessingContext source, IOrderedDither dither, - ReadOnlySpan palette, + ReadOnlyMemory palette, Rectangle rectangle) => source.ApplyProcessor(new OrderedDitherPaletteProcessor(dither, palette), rectangle); } diff --git a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs index 5436a7733f..e0b79c2b20 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs @@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// The error diffuser /// The threshold to split the image. Must be between 0 and 1. /// The palette to select substitute colors from. - public ErrorDiffusionPaletteProcessor(IErrorDiffuser diffuser, float threshold, ReadOnlySpan palette) + public ErrorDiffusionPaletteProcessor(IErrorDiffuser diffuser, float threshold, ReadOnlyMemory palette) : base(palette) { Guard.NotNull(diffuser, nameof(diffuser)); diff --git a/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor.cs b/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor.cs index ba7c1e9980..ac6554d4c1 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor.cs @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// /// The ordered ditherer. /// The palette to select substitute colors from. - public OrderedDitherPaletteProcessor(IOrderedDither dither, ReadOnlySpan palette) + public OrderedDitherPaletteProcessor(IOrderedDither dither, ReadOnlyMemory palette) : base(palette) => this.Dither = dither ?? throw new ArgumentNullException(nameof(dither)); /// diff --git a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor.cs b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor.cs index 904d026345..de798b64bc 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor.cs @@ -12,23 +12,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// public abstract class PaletteDitherProcessor : IImageProcessor { - private readonly Color[] palette; - /// /// Initializes a new instance of the class. /// /// The palette to select substitute colors from. - protected PaletteDitherProcessor(ReadOnlySpan palette) + protected PaletteDitherProcessor(ReadOnlyMemory palette) { - // This shouldn't be a perf issue: - // these arrays are small, and created with low frequency. - this.palette = palette.ToArray(); + this.Palette = palette; } /// /// Gets the palette to select substitute colors from. /// - public ReadOnlySpan Palette => this.palette; + public ReadOnlyMemory Palette { get; } /// public abstract IImageProcessor CreatePixelSpecificProcessor() diff --git a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs index 334eab833c..205b589b1e 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs @@ -20,13 +20,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering { private readonly Dictionary> cache = new Dictionary>(); + private TPixel[] palette; + /// /// The vector representation of the image palette. /// private Vector4[] paletteVector; - private TPixel[] palette; - /// /// Initializes a new instance of the class. /// @@ -37,6 +37,33 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering protected PaletteDitherProcessor Definition { get; } + protected override void BeforeFrameApply( + ImageFrame source, + Rectangle sourceRectangle, + Configuration configuration) + { + base.BeforeFrameApply(source, sourceRectangle, configuration); + + // Lazy init palette: + if (this.palette is null) + { + ReadOnlySpan sourcePalette = this.Definition.Palette.Span; + this.palette = new TPixel[sourcePalette.Length]; + Color.ToPixel(configuration, sourcePalette, this.palette); + } + + // Lazy init paletteVector: + if (this.paletteVector is null) + { + this.paletteVector = new Vector4[this.palette.Length]; + PixelOperations.Instance.ToVector4( + configuration, + (ReadOnlySpan)this.palette, + (Span)this.paletteVector, + PixelConversionModifiers.Scale); + } + } + /// /// Returns the two closest colors from the palette calculated via Euclidean distance in the Rgba space. /// @@ -88,25 +115,5 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering return pair; } - - protected override void BeforeFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - { - base.BeforeFrameApply(source, sourceRectangle, configuration); - - // Lazy init palette: - if (this.palette is null) - { - ReadOnlySpan sourcePalette = this.Definition.Palette; - this.palette = new TPixel[sourcePalette.Length]; - Color.ToPixel(configuration, sourcePalette, this.palette); - } - - // Lazy init paletteVector: - if (this.paletteVector is null) - { - this.paletteVector = new Vector4[this.palette.Length]; - PixelOperations.Instance.ToVector4(configuration, (ReadOnlySpan)this.palette, (Span)this.paletteVector, PixelConversionModifiers.Scale); - } - } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs index dcdddb217c..255be5ed59 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs @@ -54,7 +54,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays } /// - /// Gets the options effecting blending and composition + /// Gets the options effecting blending and composition. /// public GraphicsOptions GraphicsOptions { get; } diff --git a/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizerBase{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizer{TPixel}.cs similarity index 81% rename from src/ImageSharp/Processing/Processors/Quantization/FrameQuantizerBase{TPixel}.cs rename to src/ImageSharp/Processing/Processors/Quantization/FrameQuantizer{TPixel}.cs index f23343f6d7..6d8136513f 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizerBase{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizer{TPixel}.cs @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// The base class for all implementations /// /// The pixel format. - public abstract class FrameQuantizerBase : IFrameQuantizer + public abstract class FrameQuantizer : IFrameQuantizer where TPixel : struct, IPixel { /// @@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization private Vector4[] paletteVector; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The quantizer /// @@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// only call the method. /// If two passes are required, the code will also call . /// - protected FrameQuantizerBase(IQuantizer quantizer, bool singlePass) + protected FrameQuantizer(IQuantizer quantizer, bool singlePass) { Guard.NotNull(quantizer, nameof(quantizer)); @@ -52,6 +52,25 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization this.Dither = this.Diffuser != null; this.singlePass = singlePass; } + + /// + /// Initializes a new instance of the class. + /// + /// The diffuser + /// + /// If true, the quantization process only needs to loop through the source pixels once + /// + /// + /// If you construct this class with a true for , then the code will + /// only call the method. + /// If two passes are required, the code will also call . + /// + protected FrameQuantizer(IErrorDiffuser diffuser, bool singlePass) + { + this.Diffuser = diffuser; + this.Dither = this.Diffuser != null; + this.singlePass = singlePass; + } /// public bool Dither { get; } @@ -77,22 +96,28 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization } // Collect the palette. Required before the second pass runs. - TPixel[] palette = this.GetPalette(); + ReadOnlyMemory palette = this.GetPalette(); this.paletteVector = new Vector4[palette.Length]; - PixelOperations.Instance.ToVector4(image.Configuration, (ReadOnlySpan)palette, (Span)this.paletteVector, PixelConversionModifiers.Scale); - var quantizedFrame = new QuantizedFrame(image.MemoryAllocator, width, height, palette); + PixelOperations.Instance.ToVector4(image.Configuration, palette.Span, (Span)this.paletteVector, PixelConversionModifiers.Scale); + + // TODO: Pass ReadOnlyMemory instead of array! + var quantizedFrame = new QuantizedFrame( + image.MemoryAllocator, + width, + height, + palette.Span.ToArray()); if (this.Dither) { // We clone the image as we don't want to alter the original via dithering. using (ImageFrame clone = image.Clone()) { - this.SecondPass(clone, quantizedFrame.GetPixelSpan(), palette, width, height); + this.SecondPass(clone, quantizedFrame.GetPixelSpan(), palette.Span, width, height); } } else { - this.SecondPass(image, quantizedFrame.GetPixelSpan(), palette, width, height); + this.SecondPass(image, quantizedFrame.GetPixelSpan(), palette.Span, width, height); } return quantizedFrame; @@ -134,7 +159,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// /// /// - protected abstract TPixel[] GetPalette(); + protected abstract ReadOnlyMemory GetPalette(); /// /// Returns the index of the first instance of the transparent color in the palette. diff --git a/src/ImageSharp/Processing/Processors/Quantization/OctreeFrameQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/OctreeFrameQuantizer{TPixel}.cs index dd56375f63..85a4d20295 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/OctreeFrameQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/OctreeFrameQuantizer{TPixel}.cs @@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// /// /// The pixel format. - internal sealed class OctreeFrameQuantizer : FrameQuantizerBase + internal sealed class OctreeFrameQuantizer : FrameQuantizer where TPixel : struct, IPixel { /// @@ -136,10 +136,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization } } - internal TPixel[] AotGetPalette() => this.GetPalette(); + internal ReadOnlyMemory AotGetPalette() => this.GetPalette(); /// - protected override TPixel[] GetPalette() => this.octree.Palletize(this.colors); + protected override ReadOnlyMemory GetPalette() => this.octree.Palletize(this.colors); /// /// Process the pixel in the second pass of the algorithm. diff --git a/src/ImageSharp/Processing/Processors/Quantization/PaletteFrameQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/PaletteFrameQuantizer{TPixel}.cs index f8a19f8c40..265c343e68 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/PaletteFrameQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/PaletteFrameQuantizer{TPixel}.cs @@ -6,6 +6,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors.Dithering; namespace SixLabors.ImageSharp.Processing.Processors.Quantization { @@ -14,21 +15,21 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// /// /// The pixel format. - internal sealed class PaletteFrameQuantizer : FrameQuantizerBase + internal sealed class PaletteFrameQuantizer : FrameQuantizer where TPixel : struct, IPixel { /// /// The reduced image palette. /// - private readonly TPixel[] palette; + private readonly ReadOnlyMemory palette; /// /// Initializes a new instance of the class. /// - /// The palette quantizer. + /// The palette quantizer. /// An array of all colors in the palette. - public PaletteFrameQuantizer(IQuantizer quantizer, TPixel[] colors) - : base(quantizer, true) => this.palette = colors; + public PaletteFrameQuantizer(IErrorDiffuser diffuser, ReadOnlyMemory colors) + : base(diffuser, true) => this.palette = colors; /// protected override void SecondPass( @@ -85,7 +86,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected override TPixel[] GetPalette() => this.palette; + protected override ReadOnlyMemory GetPalette() => this.palette; /// /// Process the pixel in the second pass of the algorithm diff --git a/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs b/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs index 6b2be3d038..ba434a4b21 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; + using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Dithering; @@ -14,61 +15,65 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// By default the quantizer uses dithering. /// /// - public abstract class PaletteQuantizer : IQuantizer + public class PaletteQuantizer : IQuantizer { /// /// Initializes a new instance of the class. /// - protected PaletteQuantizer() - : this(true) + /// The palette. + public PaletteQuantizer(ReadOnlyMemory palette) + : this(palette, true) { } /// /// Initializes a new instance of the class. /// + /// The palette. /// Whether to apply dithering to the output image - protected PaletteQuantizer(bool dither) - : this(GetDiffuser(dither)) + public PaletteQuantizer(ReadOnlyMemory palette, bool dither) + : this(palette, GetDiffuser(dither)) { } /// /// Initializes a new instance of the class. /// + /// The palette. /// The error diffusion algorithm, if any, to apply to the output image - protected PaletteQuantizer(IErrorDiffuser diffuser) => this.Diffuser = diffuser; + public PaletteQuantizer(ReadOnlyMemory palette, IErrorDiffuser diffuser) + { + this.Palette = palette; + this.Diffuser = diffuser; + } /// public IErrorDiffuser Diffuser { get; } - /// - public abstract IFrameQuantizer CreateFrameQuantizer(Configuration configuration) - where TPixel : struct, IPixel; - - /// - public abstract IFrameQuantizer CreateFrameQuantizer(Configuration configuration, int maxColors) - where TPixel : struct, IPixel; - /// - /// Creates the generic frame quantizer. + /// Gets the palette. /// - /// The pixel format. - /// The to configure internal operations. - /// The color palette. - /// The maximum number of colors to hold in the color palette. - /// The - protected IFrameQuantizer CreateFrameQuantizer(Configuration configuration, TPixel[] palette, int maxColors) + public ReadOnlyMemory Palette { get; } + + /// + public IFrameQuantizer CreateFrameQuantizer(Configuration configuration) where TPixel : struct, IPixel { - int max = Math.Min(QuantizerConstants.MaxColors, Math.Min(maxColors, palette.Length)); + TPixel[] palette = new TPixel[this.Palette.Length]; + Color.ToPixel(configuration, this.Palette.Span, palette.AsSpan()); + return new PaletteFrameQuantizer(this.Diffuser, palette); + } - if (max != palette.Length) - { - return new PaletteFrameQuantizer(this, palette.AsSpan(0, max).ToArray()); - } + /// + public IFrameQuantizer CreateFrameQuantizer(Configuration configuration, int maxColors) + where TPixel : struct, IPixel + { + maxColors = maxColors.Clamp(QuantizerConstants.MinColors, QuantizerConstants.MaxColors); + int max = Math.Min(maxColors, this.Palette.Length); - return new PaletteFrameQuantizer(this, palette); + TPixel[] palette = new TPixel[max]; + Color.ToPixel(configuration, this.Palette.Span.Slice(0, max), palette.AsSpan()); + return new PaletteFrameQuantizer(this.Diffuser, palette); } private static IErrorDiffuser GetDiffuser(bool dither) => dither ? KnownDiffusers.FloydSteinberg : null; diff --git a/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer{TPixel}.cs deleted file mode 100644 index a350adfc0c..0000000000 --- a/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer{TPixel}.cs +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing.Processors.Dithering; - -namespace SixLabors.ImageSharp.Processing.Processors.Quantization -{ - /// - /// A generic palette quantizer. - /// - /// The pixel format. - public class PaletteQuantizer : IQuantizer - where TPixel : struct, IPixel - { - /// - /// Initializes a new instance of the class. - /// - /// The color palette to use. - public PaletteQuantizer(TPixel[] palette) - : this(palette, true) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The color palette to use. - /// Whether to apply dithering to the output image - public PaletteQuantizer(TPixel[] palette, bool dither) - : this(palette, GetDiffuser(dither)) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The color palette to use. - /// The error diffusion algorithm, if any, to apply to the output image - public PaletteQuantizer(TPixel[] palette, IErrorDiffuser diffuser) - { - Guard.MustBeBetweenOrEqualTo(palette.Length, QuantizerConstants.MinColors, QuantizerConstants.MaxColors, nameof(palette)); - this.Palette = palette; - this.Diffuser = diffuser; - } - - /// - public IErrorDiffuser Diffuser { get; } - - /// - /// Gets the palette. - /// - public TPixel[] Palette { get; } - - /// - /// Creates the generic frame quantizer. - /// - /// The to configure internal operations. - /// The . - public IFrameQuantizer CreateFrameQuantizer(Configuration configuration) - => ((IQuantizer)this).CreateFrameQuantizer(configuration); - - /// - /// Creates the generic frame quantizer. - /// - /// The to configure internal operations. - /// The maximum number of colors to hold in the color palette. - /// The . - public IFrameQuantizer CreateFrameQuantizer(Configuration configuration, int maxColors) - => ((IQuantizer)this).CreateFrameQuantizer(configuration, maxColors); - - /// - IFrameQuantizer IQuantizer.CreateFrameQuantizer(Configuration configuration) - { - if (!typeof(TPixel).Equals(typeof(TPixel1))) - { - throw new InvalidOperationException("Generic method type must be the same as class type."); - } - - TPixel[] paletteRef = this.Palette; - return new PaletteFrameQuantizer(this, Unsafe.As(ref paletteRef)); - } - - /// - IFrameQuantizer IQuantizer.CreateFrameQuantizer(Configuration configuration, int maxColors) - { - if (!typeof(TPixel).Equals(typeof(TPixel1))) - { - throw new InvalidOperationException("Generic method type must be the same as class type."); - } - - TPixel[] paletteRef = this.Palette; - TPixel1[] castPalette = Unsafe.As(ref paletteRef); - - maxColors = maxColors.Clamp(QuantizerConstants.MinColors, QuantizerConstants.MaxColors); - int max = Math.Min(maxColors, castPalette.Length); - - if (max != castPalette.Length) - { - return new PaletteFrameQuantizer(this, castPalette.AsSpan(0, max).ToArray()); - } - - return new PaletteFrameQuantizer(this, castPalette); - } - - private static IErrorDiffuser GetDiffuser(bool dither) => dither ? KnownDiffusers.FloydSteinberg : null; - } -} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Quantization/WebSafePaletteQuantizer.cs b/src/ImageSharp/Processing/Processors/Quantization/WebSafePaletteQuantizer.cs index 93630a9166..3f6dfde281 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/WebSafePaletteQuantizer.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/WebSafePaletteQuantizer.cs @@ -15,6 +15,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// Initializes a new instance of the class. /// public WebSafePaletteQuantizer() + : this(true) { } @@ -23,7 +24,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// /// Whether to apply dithering to the output image public WebSafePaletteQuantizer(bool dither) - : base(dither) + : base(Color.WebSafePalette, dither) { } @@ -32,16 +33,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// /// The error diffusion algorithm, if any, to apply to the output image public WebSafePaletteQuantizer(IErrorDiffuser diffuser) - : base(diffuser) + : base(Color.WebSafePalette, diffuser) { } - - /// - public override IFrameQuantizer CreateFrameQuantizer(Configuration configuration) - => this.CreateFrameQuantizer(configuration, NamedColors.WebSafePalette.Length); - - /// - public override IFrameQuantizer CreateFrameQuantizer(Configuration configuration, int maxColors) - => this.CreateFrameQuantizer(configuration, NamedColors.WebSafePalette, maxColors); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Quantization/WernerPaletteQuantizer.cs b/src/ImageSharp/Processing/Processors/Quantization/WernerPaletteQuantizer.cs index 2ff9f5090c..d659ecabf7 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/WernerPaletteQuantizer.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/WernerPaletteQuantizer.cs @@ -16,6 +16,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// Initializes a new instance of the class. /// public WernerPaletteQuantizer() + : this(true) { } @@ -24,7 +25,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// /// Whether to apply dithering to the output image public WernerPaletteQuantizer(bool dither) - : base(dither) + : base(Color.WernerPalette, dither) { } @@ -33,16 +34,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// /// The error diffusion algorithm, if any, to apply to the output image public WernerPaletteQuantizer(IErrorDiffuser diffuser) - : base(diffuser) + : base(Color.WernerPalette, diffuser) { } - - /// - public override IFrameQuantizer CreateFrameQuantizer(Configuration configuration) - => this.CreateFrameQuantizer(configuration, NamedColors.WernerPalette.Length); - - /// - public override IFrameQuantizer CreateFrameQuantizer(Configuration configuration, int maxColors) - => this.CreateFrameQuantizer(configuration, NamedColors.WernerPalette, maxColors); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs index 1f1513adf1..95918f564a 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs @@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// /// /// The pixel format. - internal sealed class WuFrameQuantizer : FrameQuantizerBase + internal sealed class WuFrameQuantizer : FrameQuantizer where TPixel : struct, IPixel { // The following two variables determine the amount of bits to preserve when calculating the histogram. @@ -171,10 +171,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization this.tag?.Dispose(); } - internal TPixel[] AotGetPalette() => this.GetPalette(); + internal ReadOnlyMemory AotGetPalette() => this.GetPalette(); /// - protected override TPixel[] GetPalette() + protected override ReadOnlyMemory GetPalette() { if (this.palette is null) { diff --git a/tests/ImageSharp.Tests/Processing/Dithering/DitherTest.cs b/tests/ImageSharp.Tests/Processing/Dithering/DitherTest.cs index 59dcde2a63..d91688cd2c 100644 --- a/tests/ImageSharp.Tests/Processing/Dithering/DitherTest.cs +++ b/tests/ImageSharp.Tests/Processing/Dithering/DitherTest.cs @@ -43,7 +43,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Binarization this.operations.Dither(this.orderedDither); OrderedDitherPaletteProcessor p = this.Verify(); Assert.Equal(this.orderedDither, p.Dither); - Assert.Equal(Color.WebSafePalette.ToArray(), p.Palette); + Assert.Equal(Color.WebSafePalette, p.Palette); } [Fact] diff --git a/tests/ImageSharp.Tests/Processing/Processors/Quantization/PaletteQuantizerTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Quantization/PaletteQuantizerTests.cs index a4e6edd53e..05ce700476 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Quantization/PaletteQuantizerTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Quantization/PaletteQuantizerTests.cs @@ -11,21 +11,21 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Quantization { public class PaletteQuantizerTests { - private static readonly Rgba32[] Rgb = new Rgba32[] { Rgba32.Red, Rgba32.Green, Rgba32.Blue }; + private static readonly Color[] Rgb = new Color[] { Rgba32.Red, Rgba32.Green, Rgba32.Blue }; [Fact] public void PaletteQuantizerConstructor() { - var quantizer = new PaletteQuantizer(Rgb); + var quantizer = new PaletteQuantizer(Rgb); Assert.Equal(Rgb, quantizer.Palette); Assert.Equal(KnownDiffusers.FloydSteinberg, quantizer.Diffuser); - quantizer = new PaletteQuantizer(Rgb, false); + quantizer = new PaletteQuantizer(Rgb, false); Assert.Equal(Rgb, quantizer.Palette); Assert.Null(quantizer.Diffuser); - quantizer = new PaletteQuantizer(Rgb, KnownDiffusers.Atkinson); + quantizer = new PaletteQuantizer(Rgb, KnownDiffusers.Atkinson); Assert.Equal(Rgb, quantizer.Palette); Assert.Equal(KnownDiffusers.Atkinson, quantizer.Diffuser); } @@ -33,35 +33,27 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Quantization [Fact] public void PaletteQuantizerCanCreateFrameQuantizer() { - var quantizer = new PaletteQuantizer(Rgb); - IFrameQuantizer frameQuantizer = quantizer.CreateFrameQuantizer(Configuration.Default); + var quantizer = new PaletteQuantizer(Rgb); + IFrameQuantizer frameQuantizer = quantizer.CreateFrameQuantizer(Configuration.Default); Assert.NotNull(frameQuantizer); Assert.True(frameQuantizer.Dither); Assert.Equal(KnownDiffusers.FloydSteinberg, frameQuantizer.Diffuser); - quantizer = new PaletteQuantizer(Rgb, false); - frameQuantizer = quantizer.CreateFrameQuantizer(Configuration.Default); + quantizer = new PaletteQuantizer(Rgb, false); + frameQuantizer = quantizer.CreateFrameQuantizer(Configuration.Default); Assert.NotNull(frameQuantizer); Assert.False(frameQuantizer.Dither); Assert.Null(frameQuantizer.Diffuser); - quantizer = new PaletteQuantizer(Rgb, KnownDiffusers.Atkinson); - frameQuantizer = quantizer.CreateFrameQuantizer(Configuration.Default); + quantizer = new PaletteQuantizer(Rgb, KnownDiffusers.Atkinson); + frameQuantizer = quantizer.CreateFrameQuantizer(Configuration.Default); Assert.NotNull(frameQuantizer); Assert.True(frameQuantizer.Dither); Assert.Equal(KnownDiffusers.Atkinson, frameQuantizer.Diffuser); } - [Fact] - public void PaletteQuantizerThrowsOnInvalidGenericMethodCall() - { - var quantizer = new PaletteQuantizer(Rgb); - - Assert.Throws(() => ((IQuantizer)quantizer).CreateFrameQuantizer(Configuration.Default)); - } - [Fact] public void KnownQuantizersWebSafeTests() { From b68699c82bf968bf167322635e57d4a5d49ae657 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 10 May 2019 23:11:04 +0200 Subject: [PATCH 159/223] QuantizedFrame using ReadOnlyMemory --- src/ImageSharp/Formats/Gif/GifEncoderCore.cs | 4 +- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 2 +- .../Quantization/FrameQuantizer{TPixel}.cs | 84 +++++++++---------- .../Quantization/QuantizeProcessor{TPixel}.cs | 5 +- .../Quantization/QuantizedFrame{TPixel}.cs | 4 +- .../Quantization/QuantizedImageTests.cs | 7 +- .../Quantization/WuQuantizerTests.cs | 10 ++- 7 files changed, 62 insertions(+), 54 deletions(-) diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs index 2770790a28..9270bb6b1f 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs @@ -233,7 +233,7 @@ namespace SixLabors.ImageSharp.Formats.Gif { Span rgbaSpan = rgbaBuffer.GetSpan(); ref Rgba32 paletteRef = ref MemoryMarshal.GetReference(rgbaSpan); - PixelOperations.Instance.ToRgba32(this.configuration, quantized.Palette, rgbaSpan); + PixelOperations.Instance.ToRgba32(this.configuration, quantized.Palette.Span, rgbaSpan); for (int i = quantized.Palette.Length - 1; i >= 0; i--) { @@ -436,7 +436,7 @@ namespace SixLabors.ImageSharp.Formats.Gif { PixelOperations.Instance.ToRgb24Bytes( this.configuration, - image.Palette.AsSpan(), + image.Palette.Span, colorTable.GetSpan(), pixelCount); stream.Write(colorTable.Array, 0, colorTableLength); diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 7415b07532..db0c7f2585 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -666,7 +666,7 @@ namespace SixLabors.ImageSharp.Formats.Png where TPixel : struct, IPixel { // Grab the palette and write it to the stream. - TPixel[] palette = quantized.Palette; + ReadOnlySpan palette = quantized.Palette.Span; int paletteLength = Math.Min(palette.Length, 256); int colorTableLength = paletteLength * 3; bool anyAlpha = false; diff --git a/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizer{TPixel}.cs index 6d8136513f..eac31b21d0 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizer{TPixel}.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Numerics; using System.Runtime.CompilerServices; + using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Dithering; @@ -52,7 +53,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization this.Dither = this.Diffuser != null; this.singlePass = singlePass; } - + /// /// Initializes a new instance of the class. /// @@ -73,10 +74,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization } /// - public bool Dither { get; } + public IErrorDiffuser Diffuser { get; } /// - public IErrorDiffuser Diffuser { get; } + public bool Dither { get; } + + /// + public virtual void Dispose() + { + } /// public virtual QuantizedFrame QuantizeFrame(ImageFrame image) @@ -98,14 +104,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization // Collect the palette. Required before the second pass runs. ReadOnlyMemory palette = this.GetPalette(); this.paletteVector = new Vector4[palette.Length]; - PixelOperations.Instance.ToVector4(image.Configuration, palette.Span, (Span)this.paletteVector, PixelConversionModifiers.Scale); + PixelOperations.Instance.ToVector4( + image.Configuration, + palette.Span, + (Span)this.paletteVector, + PixelConversionModifiers.Scale); - // TODO: Pass ReadOnlyMemory instead of array! - var quantizedFrame = new QuantizedFrame( - image.MemoryAllocator, - width, - height, - palette.Span.ToArray()); + var quantizedFrame = new QuantizedFrame(image.MemoryAllocator, width, height, palette); if (this.Dither) { @@ -123,11 +128,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization return quantizedFrame; } - /// - public virtual void Dispose() - { - } - /// /// Execute the first pass through the pixels in the image to create the palette. /// @@ -139,19 +139,22 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization } /// - /// Execute a second pass through the image to assign the pixels to a palette entry. + /// Returns the closest color from the palette to the given color by calculating the + /// Euclidean distance in the Rgba colorspace. /// - /// The source image. - /// The output pixel array. - /// The output color palette. - /// The width in pixels of the image. - /// The height in pixels of the image. - protected abstract void SecondPass( - ImageFrame source, - Span output, - ReadOnlySpan palette, - int width, - int height); + /// The color. + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected byte GetClosestPixel(ref TPixel pixel) + { + // Check if the color is in the lookup table + if (this.distanceCache.TryGetValue(pixel, out byte value)) + { + return value; + } + + return this.GetClosestPixelSlow(ref pixel); + } /// /// Retrieve the palette for the quantized image. @@ -185,22 +188,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization } /// - /// Returns the closest color from the palette to the given color by calculating the - /// Euclidean distance in the Rgba colorspace. + /// Execute a second pass through the image to assign the pixels to a palette entry. /// - /// The color. - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected byte GetClosestPixel(ref TPixel pixel) - { - // Check if the color is in the lookup table - if (this.distanceCache.TryGetValue(pixel, out byte value)) - { - return value; - } - - return this.GetClosestPixelSlow(ref pixel); - } + /// The source image. + /// The output pixel array. + /// The output color palette. + /// The width in pixels of the image. + /// The height in pixels of the image. + protected abstract void SecondPass( + ImageFrame source, + Span output, + ReadOnlySpan palette, + int width, + int height); [MethodImpl(MethodImplOptions.NoInlining)] private byte GetClosestPixelSlow(ref TPixel pixel) diff --git a/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs index b52343a2ae..4f134d98d7 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs @@ -43,12 +43,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization { Span row = source.GetPixelRowSpan(y); ReadOnlySpan quantizedPixelSpan = quantized.GetPixelSpan(); + + ReadOnlySpan paletteSpan = quantized.Palette.Span; + int yy = y * source.Width; for (int x = 0; x < source.Width; x++) { int i = x + yy; - row[x] = quantized.Palette[Math.Min(paletteCount, quantizedPixelSpan[i])]; + row[x] = paletteSpan[Math.Min(paletteCount, quantizedPixelSpan[i])]; } } } diff --git a/src/ImageSharp/Processing/Processors/Quantization/QuantizedFrame{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/QuantizedFrame{TPixel}.cs index 38862ef446..ae5b5dda86 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/QuantizedFrame{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/QuantizedFrame{TPixel}.cs @@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// The image width. /// The image height. /// The color palette. - public QuantizedFrame(MemoryAllocator memoryAllocator, int width, int height, TPixel[] palette) + public QuantizedFrame(MemoryAllocator memoryAllocator, int width, int height, ReadOnlyMemory palette) { Guard.MustBeGreaterThan(width, 0, nameof(width)); Guard.MustBeGreaterThan(height, 0, nameof(height)); @@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// /// Gets the color palette of this . /// - public TPixel[] Palette { get; private set; } + public ReadOnlyMemory Palette { get; private set; } /// /// Gets the pixels of this . diff --git a/tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs b/tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs index a0d7869e39..b0de058f80 100644 --- a/tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs +++ b/tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; + using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Quantization; @@ -85,9 +87,10 @@ namespace SixLabors.ImageSharp.Tests // Transparent pixels are much more likely to be found at the end of a palette int index = -1; Rgba32 trans = default; - for (int i = quantized.Palette.Length - 1; i >= 0; i--) + ReadOnlySpan paletteSpan = quantized.Palette.Span; + for (int i = paletteSpan.Length - 1; i >= 0; i--) { - quantized.Palette[i].ToRgba32(ref trans); + paletteSpan[i].ToRgba32(ref trans); if (trans.Equals(default)) { diff --git a/tests/ImageSharp.Tests/Quantization/WuQuantizerTests.cs b/tests/ImageSharp.Tests/Quantization/WuQuantizerTests.cs index 3eacd74ea1..716273699e 100644 --- a/tests/ImageSharp.Tests/Quantization/WuQuantizerTests.cs +++ b/tests/ImageSharp.Tests/Quantization/WuQuantizerTests.cs @@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Tests.Quantization Assert.Equal(1, result.Palette.Length); Assert.Equal(1, result.GetPixelSpan().Length); - Assert.Equal(Rgba32.Black, result.Palette[0]); + Assert.Equal(Rgba32.Black, result.Palette.Span[0]); Assert.Equal(0, result.GetPixelSpan()[0]); } } @@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.Tests.Quantization Assert.Equal(1, result.Palette.Length); Assert.Equal(1, result.GetPixelSpan().Length); - Assert.Equal(default, result.Palette[0]); + Assert.Equal(default, result.Palette.Span[0]); Assert.Equal(0, result.GetPixelSpan()[0]); } } @@ -82,6 +82,7 @@ namespace SixLabors.ImageSharp.Tests.Quantization var actualImage = new Image(1, 256); + ReadOnlySpan paletteSpan = result.Palette.Span; int paletteCount = result.Palette.Length - 1; for (int y = 0; y < actualImage.Height; y++) { @@ -92,7 +93,7 @@ namespace SixLabors.ImageSharp.Tests.Quantization for (int x = 0; x < actualImage.Width; x++) { int i = x + yy; - row[x] = result.Palette[Math.Min(paletteCount, quantizedPixelSpan[i])]; + row[x] = paletteSpan[Math.Min(paletteCount, quantizedPixelSpan[i])]; } } @@ -146,6 +147,7 @@ namespace SixLabors.ImageSharp.Tests.Quantization Assert.Equal(4 * 8, result.Palette.Length); Assert.Equal(256, result.GetPixelSpan().Length); + ReadOnlySpan paletteSpan = result.Palette.Span; int paletteCount = result.Palette.Length - 1; for (int y = 0; y < actualImage.Height; y++) { @@ -156,7 +158,7 @@ namespace SixLabors.ImageSharp.Tests.Quantization for (int x = 0; x < actualImage.Width; x++) { int i = x + yy; - row[x] = result.Palette[Math.Min(paletteCount, quantizedPixelSpan[i])]; + row[x] = paletteSpan[Math.Min(paletteCount, quantizedPixelSpan[i])]; } } } From 63ffdcb18f4281b3c5fe7b0a39df40265580a0dd Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 10 May 2019 23:31:46 +0200 Subject: [PATCH 160/223] Correct readonly-semantics for QuantizedFrame --- src/ImageSharp/Advanced/AotCompilerTools.cs | 2 +- src/ImageSharp/Formats/Gif/GifEncoderCore.cs | 2 +- src/ImageSharp/Formats/Gif/LzwEncoder.cs | 6 ++--- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 2 +- .../Quantization/FrameQuantizer{TPixel}.cs | 7 ++--- .../Quantization/QuantizeProcessor{TPixel}.cs | 2 +- .../Quantization/QuantizedFrame{TPixel}.cs | 11 +++++--- .../Quantization/WuFrameQuantizer{TPixel}.cs | 26 ++++++++++++------- .../Processors/Quantization/WuQuantizer.cs | 8 ++++-- 9 files changed, 41 insertions(+), 25 deletions(-) diff --git a/src/ImageSharp/Advanced/AotCompilerTools.cs b/src/ImageSharp/Advanced/AotCompilerTools.cs index eb6991e6a1..82df7304e5 100644 --- a/src/ImageSharp/Advanced/AotCompilerTools.cs +++ b/src/ImageSharp/Advanced/AotCompilerTools.cs @@ -104,7 +104,7 @@ namespace SixLabors.ImageSharp.Advanced private static void AotCompileWuQuantizer() where TPixel : struct, IPixel { - var test = new WuFrameQuantizer(new WuQuantizer(false)); + var test = new WuFrameQuantizer(Configuration.Default.MemoryAllocator, new WuQuantizer(false)); test.QuantizeFrame(new ImageFrame(Configuration.Default, 1, 1)); test.AotGetPalette(); } diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs index 9270bb6b1f..ac97570941 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs @@ -164,7 +164,7 @@ namespace SixLabors.ImageSharp.Formats.Gif using (QuantizedFrame paletteQuantized = palleteFrameQuantizer.QuantizeFrame(frame)) { this.WriteImageData(paletteQuantized, stream); - } + } } } } diff --git a/src/ImageSharp/Formats/Gif/LzwEncoder.cs b/src/ImageSharp/Formats/Gif/LzwEncoder.cs index 2d32fd23aa..b4c80a3da1 100644 --- a/src/ImageSharp/Formats/Gif/LzwEncoder.cs +++ b/src/ImageSharp/Formats/Gif/LzwEncoder.cs @@ -180,7 +180,7 @@ namespace SixLabors.ImageSharp.Formats.Gif /// /// The span of indexed pixels. /// The stream to write to. - public void Encode(Span indexedPixels, Stream stream) + public void Encode(ReadOnlySpan indexedPixels, Stream stream) { // Write "initial code size" byte stream.WriteByte((byte)this.initialCodeSize); @@ -251,7 +251,7 @@ namespace SixLabors.ImageSharp.Formats.Gif /// The span of indexed pixels. /// The initial bits. /// The stream to write to. - private void Compress(Span indexedPixels, int intialBits, Stream stream) + private void Compress(ReadOnlySpan indexedPixels, int intialBits, Stream stream) { int fcode; int c; @@ -375,7 +375,7 @@ namespace SixLabors.ImageSharp.Formats.Gif /// The /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - private int NextPixel(Span indexedPixels) + private int NextPixel(ReadOnlySpan indexedPixels) { return indexedPixels[this.position++] & 0xFF; } diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index db0c7f2585..7bdced536b 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -676,7 +676,7 @@ namespace SixLabors.ImageSharp.Formats.Png { ref byte colorTableRef = ref MemoryMarshal.GetReference(colorTable.GetSpan()); ref byte alphaTableRef = ref MemoryMarshal.GetReference(alphaTable.GetSpan()); - Span quantizedSpan = quantized.GetPixelSpan(); + ReadOnlySpan quantizedSpan = quantized.GetPixelSpan(); Rgba32 rgba = default; diff --git a/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizer{TPixel}.cs index eac31b21d0..d753b08a83 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizer{TPixel}.cs @@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization } /// - public virtual QuantizedFrame QuantizeFrame(ImageFrame image) + public QuantizedFrame QuantizeFrame(ImageFrame image) { Guard.NotNull(image, nameof(image)); @@ -112,17 +112,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization var quantizedFrame = new QuantizedFrame(image.MemoryAllocator, width, height, palette); + Span pixelSpan = quantizedFrame.GetWritablePixelSpan(); if (this.Dither) { // We clone the image as we don't want to alter the original via dithering. using (ImageFrame clone = image.Clone()) { - this.SecondPass(clone, quantizedFrame.GetPixelSpan(), palette.Span, width, height); + this.SecondPass(clone, pixelSpan, palette.Span, width, height); } } else { - this.SecondPass(image, quantizedFrame.GetPixelSpan(), palette.Span, width, height); + this.SecondPass(image, pixelSpan, palette.Span, width, height); } return quantizedFrame; diff --git a/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs index 4f134d98d7..21071dd8a0 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs @@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization ReadOnlySpan quantizedPixelSpan = quantized.GetPixelSpan(); ReadOnlySpan paletteSpan = quantized.Palette.Span; - + int yy = y * source.Width; for (int x = 0; x < source.Width; x++) diff --git a/src/ImageSharp/Processing/Processors/Quantization/QuantizedFrame{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/QuantizedFrame{TPixel}.cs index ae5b5dda86..a4d06ac2e2 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/QuantizedFrame{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/QuantizedFrame{TPixel}.cs @@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// The image width. /// The image height. /// The color palette. - public QuantizedFrame(MemoryAllocator memoryAllocator, int width, int height, ReadOnlyMemory palette) + internal QuantizedFrame(MemoryAllocator memoryAllocator, int width, int height, ReadOnlyMemory palette) { Guard.MustBeGreaterThan(width, 0, nameof(width)); Guard.MustBeGreaterThan(height, 0, nameof(height)); @@ -58,7 +58,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// /// The [MethodImpl(InliningOptions.ShortMethod)] - public Span GetPixelSpan() => this.pixels.GetSpan(); + public ReadOnlySpan GetPixelSpan() => this.pixels.GetSpan(); /// /// Gets the representation of the pixels as a of contiguous memory @@ -67,7 +67,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// The row. /// The [MethodImpl(InliningOptions.ShortMethod)] - public Span GetRowSpan(int rowIndex) => this.GetPixelSpan().Slice(rowIndex * this.Width, this.Width); + public ReadOnlySpan GetRowSpan(int rowIndex) => this.GetPixelSpan().Slice(rowIndex * this.Width, this.Width); /// public void Dispose() @@ -76,5 +76,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization this.pixels = null; this.Palette = null; } + + /// + /// Get the non-readonly span of pixel data so can fill it. + /// + internal Span GetWritablePixelSpan() => this.pixels.GetSpan(); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs index 95918f564a..2b1a24aee5 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs @@ -120,33 +120,31 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// /// Initializes a new instance of the class. /// + /// The . /// The wu quantizer /// /// The Wu quantizer is a two pass algorithm. The initial pass sets up the 3-D color histogram, /// the second pass quantizes a color based on the position in the histogram. /// - public WuFrameQuantizer(WuQuantizer quantizer) - : this(quantizer, quantizer.MaxColors) + public WuFrameQuantizer(MemoryAllocator memoryAllocator, WuQuantizer quantizer) + : this(memoryAllocator, quantizer, quantizer.MaxColors) { } /// /// Initializes a new instance of the class. /// + /// The . /// The wu quantizer. /// The maximum number of colors to hold in the color palette. /// /// The Wu quantizer is a two pass algorithm. The initial pass sets up the 3-D color histogram, /// the second pass quantizes a color based on the position in the histogram. /// - public WuFrameQuantizer(WuQuantizer quantizer, int maxColors) - : base(quantizer, false) => this.colors = maxColors; - - /// - public override QuantizedFrame QuantizeFrame(ImageFrame image) + public WuFrameQuantizer(MemoryAllocator memoryAllocator, WuQuantizer quantizer, int maxColors) + : base(quantizer, false) { - Guard.NotNull(image, nameof(image)); - MemoryAllocator memoryAllocator = image.MemoryAllocator; + Guard.NotNull(memoryAllocator, nameof(memoryAllocator)); this.vwt = memoryAllocator.Allocate(TableLength, AllocationOptions.Clean); this.vmr = memoryAllocator.Allocate(TableLength, AllocationOptions.Clean); @@ -156,7 +154,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization this.m2 = memoryAllocator.Allocate(TableLength, AllocationOptions.Clean); this.tag = memoryAllocator.Allocate(TableLength, AllocationOptions.Clean); - return base.QuantizeFrame(image); + this.colors = maxColors; } /// @@ -169,6 +167,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization this.vma?.Dispose(); this.m2?.Dispose(); this.tag?.Dispose(); + + this.vwt = null; + this.vmr = null; + this.vmg = null; + this.vmb = null; + this.vma = null; + this.m2 = null; + this.tag = null; } internal ReadOnlyMemory AotGetPalette() => this.GetPalette(); diff --git a/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer.cs b/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer.cs index eb8b0fec91..b80cedeb39 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer.cs @@ -72,14 +72,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// public IFrameQuantizer CreateFrameQuantizer(Configuration configuration) where TPixel : struct, IPixel - => new WuFrameQuantizer(this); + { + Guard.NotNull(configuration, nameof(configuration)); + return new WuFrameQuantizer(configuration.MemoryAllocator, this); + } /// public IFrameQuantizer CreateFrameQuantizer(Configuration configuration, int maxColors) where TPixel : struct, IPixel { + Guard.NotNull(configuration, nameof(configuration)); maxColors = maxColors.Clamp(QuantizerConstants.MinColors, QuantizerConstants.MaxColors); - return new WuFrameQuantizer(this, maxColors); + return new WuFrameQuantizer(configuration.MemoryAllocator, this, maxColors); } private static IErrorDiffuser GetDiffuser(bool dither) => dither ? KnownDiffusers.FloydSteinberg : null; From b177f62c8ab0194919110ad574f5255096ace40c Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 11 May 2019 00:11:26 +0200 Subject: [PATCH 161/223] introduce IQuantizedFrame --- src/ImageSharp/Formats/Gif/GifEncoderCore.cs | 16 ++++---- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 8 ++-- .../Quantization/FrameQuantizer{TPixel}.cs | 2 +- .../Quantization/IFrameQuantizer{TPixel}.cs | 2 +- .../Quantization/IQuantizedFrame{TPixel}.cs | 38 +++++++++++++++++++ .../Quantization/QuantizeProcessor{TPixel}.cs | 4 +- .../Quantization/QuantizedFrameExtensions.cs | 29 ++++++++++++++ .../Quantization/QuantizedFrame{TPixel}.cs | 12 +----- .../Quantization/QuantizedImageTests.cs | 6 +-- .../Quantization/WuQuantizerTests.cs | 10 ++--- 10 files changed, 92 insertions(+), 35 deletions(-) create mode 100644 src/ImageSharp/Processing/Processors/Quantization/IQuantizedFrame{TPixel}.cs create mode 100644 src/ImageSharp/Processing/Processors/Quantization/QuantizedFrameExtensions.cs diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs index ac97570941..36e27866e9 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs @@ -95,7 +95,7 @@ namespace SixLabors.ImageSharp.Formats.Gif bool useGlobalTable = this.colorTableMode == GifColorTableMode.Global; // Quantize the image returning a palette. - QuantizedFrame quantized = null; + IQuantizedFrame quantized = null; using (IFrameQuantizer frameQuantizer = this.quantizer.CreateFrameQuantizer(image.GetConfiguration())) { quantized = frameQuantizer.QuantizeFrame(image.Frames.RootFrame); @@ -141,7 +141,7 @@ namespace SixLabors.ImageSharp.Formats.Gif stream.WriteByte(GifConstants.EndIntroducer); } - private void EncodeGlobal(Image image, QuantizedFrame quantized, int transparencyIndex, Stream stream) + private void EncodeGlobal(Image image, IQuantizedFrame quantized, int transparencyIndex, Stream stream) where TPixel : struct, IPixel { for (int i = 0; i < image.Frames.Count; i++) @@ -161,7 +161,7 @@ namespace SixLabors.ImageSharp.Formats.Gif using (IFrameQuantizer palleteFrameQuantizer = new PaletteFrameQuantizer(this.quantizer.Diffuser, quantized.Palette)) { - using (QuantizedFrame paletteQuantized = palleteFrameQuantizer.QuantizeFrame(frame)) + using (IQuantizedFrame paletteQuantized = palleteFrameQuantizer.QuantizeFrame(frame)) { this.WriteImageData(paletteQuantized, stream); } @@ -170,7 +170,7 @@ namespace SixLabors.ImageSharp.Formats.Gif } } - private void EncodeLocal(Image image, QuantizedFrame quantized, Stream stream) + private void EncodeLocal(Image image, IQuantizedFrame quantized, Stream stream) where TPixel : struct, IPixel { ImageFrame previousFrame = null; @@ -222,7 +222,7 @@ namespace SixLabors.ImageSharp.Formats.Gif /// /// The . /// - private int GetTransparentIndex(QuantizedFrame quantized) + private int GetTransparentIndex(IQuantizedFrame quantized) where TPixel : struct, IPixel { // Transparent pixels are much more likely to be found at the end of a palette @@ -425,7 +425,7 @@ namespace SixLabors.ImageSharp.Formats.Gif /// The pixel format. /// The to encode. /// The stream to write to. - private void WriteColorTable(QuantizedFrame image, Stream stream) + private void WriteColorTable(IQuantizedFrame image, Stream stream) where TPixel : struct, IPixel { // The maximum number of colors for the bit depth @@ -447,9 +447,9 @@ namespace SixLabors.ImageSharp.Formats.Gif /// Writes the image pixel data to the stream. /// /// The pixel format. - /// The containing indexed pixels. + /// The containing indexed pixels. /// The stream to write to. - private void WriteImageData(QuantizedFrame image, Stream stream) + private void WriteImageData(IQuantizedFrame image, Stream stream) where TPixel : struct, IPixel { using (var encoder = new LzwEncoder(this.memoryAllocator, (byte)this.bitDepth)) diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 7bdced536b..cbc35f3c3e 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -227,7 +227,7 @@ namespace SixLabors.ImageSharp.Formats.Png stream.Write(PngConstants.HeaderBytes, 0, PngConstants.HeaderBytes.Length); - QuantizedFrame quantized = null; + IQuantizedFrame quantized = null; if (this.pngColorType == PngColorType.Palette) { byte bits = (byte)this.pngBitDepth; @@ -511,7 +511,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// The quantized pixels. Can be null. /// The row. /// The - private IManagedByteBuffer EncodePixelRow(ReadOnlySpan rowSpan, QuantizedFrame quantized, int row) + private IManagedByteBuffer EncodePixelRow(ReadOnlySpan rowSpan, IQuantizedFrame quantized, int row) where TPixel : struct, IPixel { switch (this.pngColorType) @@ -662,7 +662,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// The pixel format. /// The containing image data. /// The quantized frame. - private void WritePaletteChunk(Stream stream, QuantizedFrame quantized) + private void WritePaletteChunk(Stream stream, IQuantizedFrame quantized) where TPixel : struct, IPixel { // Grab the palette and write it to the stream. @@ -808,7 +808,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// The image. /// The quantized pixel data. Can be null. /// The stream. - private void WriteDataChunks(ImageFrame pixels, QuantizedFrame quantized, Stream stream) + private void WriteDataChunks(ImageFrame pixels, IQuantizedFrame quantized, Stream stream) where TPixel : struct, IPixel { this.bytesPerScanline = this.CalculateScanlineLength(this.width); diff --git a/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizer{TPixel}.cs index d753b08a83..e6ffecc84d 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizer{TPixel}.cs @@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization } /// - public QuantizedFrame QuantizeFrame(ImageFrame image) + public IQuantizedFrame QuantizeFrame(ImageFrame image) { Guard.NotNull(image, nameof(image)); diff --git a/src/ImageSharp/Processing/Processors/Quantization/IFrameQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/IFrameQuantizer{TPixel}.cs index f0b68b3a08..54dabab0ae 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/IFrameQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/IFrameQuantizer{TPixel}.cs @@ -31,6 +31,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// /// A representing a quantized version of the image pixels. /// - QuantizedFrame QuantizeFrame(ImageFrame image); + IQuantizedFrame QuantizeFrame(ImageFrame image); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Quantization/IQuantizedFrame{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/IQuantizedFrame{TPixel}.cs new file mode 100644 index 0000000000..42016459be --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Quantization/IQuantizedFrame{TPixel}.cs @@ -0,0 +1,38 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors.Quantization +{ + /// + /// Defines an abstraction to represent a quantized image frame where the pixels indexed by a color palette. + /// + /// The pixel format. + public interface IQuantizedFrame : IDisposable + where TPixel : struct, IPixel + { + /// + /// Gets the width of this . + /// + int Width { get; } + + /// + /// Gets the height of this . + /// + int Height { get; } + + /// + /// Gets the color palette of this . + /// + ReadOnlyMemory Palette { get; } + + /// + /// Gets the pixels of this . + /// + /// The The pixel span. + ReadOnlySpan GetPixelSpan(); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs index 21071dd8a0..5cb45e8d70 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs @@ -31,8 +31,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - using (IFrameQuantizer executor = this.quantizer.CreateFrameQuantizer(configuration)) - using (QuantizedFrame quantized = executor.QuantizeFrame(source)) + using (IFrameQuantizer frameQuantizer = this.quantizer.CreateFrameQuantizer(configuration)) + using (IQuantizedFrame quantized = frameQuantizer.QuantizeFrame(source)) { int paletteCount = quantized.Palette.Length - 1; diff --git a/src/ImageSharp/Processing/Processors/Quantization/QuantizedFrameExtensions.cs b/src/ImageSharp/Processing/Processors/Quantization/QuantizedFrameExtensions.cs new file mode 100644 index 0000000000..fa3d36e10a --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Quantization/QuantizedFrameExtensions.cs @@ -0,0 +1,29 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors.Quantization +{ + /// + /// Contains extension methods for . + /// + public static class QuantizedFrameExtensions + { + /// + /// Gets the representation of the pixels as a of contiguous memory + /// at row beginning from the the first pixel on that row. + /// + /// The . + /// The row. + /// The pixel type. + /// The pixel row as a . + [MethodImpl(InliningOptions.ShortMethod)] + public static ReadOnlySpan GetRowSpan(this IQuantizedFrame frame, int rowIndex) + where TPixel : struct, IPixel + => frame.GetPixelSpan().Slice(rowIndex * frame.Width, frame.Width); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Quantization/QuantizedFrame{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/QuantizedFrame{TPixel}.cs index a4d06ac2e2..cbea82c1f3 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/QuantizedFrame{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/QuantizedFrame{TPixel}.cs @@ -8,14 +8,13 @@ using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Memory; -// TODO: Consider pooling the TPixel palette also. For Rgba48+ this would end up on th LOH if 256 colors. namespace SixLabors.ImageSharp.Processing.Processors.Quantization { /// /// Represents a quantized image frame where the pixels indexed by a color palette. /// /// The pixel format. - public class QuantizedFrame : IDisposable + public class QuantizedFrame : IQuantizedFrame where TPixel : struct, IPixel { private IMemoryOwner pixels; @@ -60,15 +59,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization [MethodImpl(InliningOptions.ShortMethod)] public ReadOnlySpan GetPixelSpan() => this.pixels.GetSpan(); - /// - /// Gets the representation of the pixels as a of contiguous memory - /// at row beginning from the the first pixel on that row. - /// - /// The row. - /// The - [MethodImpl(InliningOptions.ShortMethod)] - public ReadOnlySpan GetRowSpan(int rowIndex) => this.GetPixelSpan().Slice(rowIndex * this.Width, this.Width); - /// public void Dispose() { diff --git a/tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs b/tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs index b0de058f80..04e9803af4 100644 --- a/tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs +++ b/tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs @@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Tests foreach (ImageFrame frame in image.Frames) { - QuantizedFrame quantized = + IQuantizedFrame quantized = quantizer.CreateFrameQuantizer(this.Configuration).QuantizeFrame(frame); int index = this.GetTransparentIndex(quantized); @@ -72,7 +72,7 @@ namespace SixLabors.ImageSharp.Tests foreach (ImageFrame frame in image.Frames) { - QuantizedFrame quantized = + IQuantizedFrame quantized = quantizer.CreateFrameQuantizer(this.Configuration).QuantizeFrame(frame); int index = this.GetTransparentIndex(quantized); @@ -81,7 +81,7 @@ namespace SixLabors.ImageSharp.Tests } } - private int GetTransparentIndex(QuantizedFrame quantized) + private int GetTransparentIndex(IQuantizedFrame quantized) where TPixel : struct, IPixel { // Transparent pixels are much more likely to be found at the end of a palette diff --git a/tests/ImageSharp.Tests/Quantization/WuQuantizerTests.cs b/tests/ImageSharp.Tests/Quantization/WuQuantizerTests.cs index 716273699e..a1de7fd4b9 100644 --- a/tests/ImageSharp.Tests/Quantization/WuQuantizerTests.cs +++ b/tests/ImageSharp.Tests/Quantization/WuQuantizerTests.cs @@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Tests.Quantization var quantizer = new WuQuantizer(false); using (var image = new Image(config, 1, 1, Rgba32.Black)) - using (QuantizedFrame result = quantizer.CreateFrameQuantizer(config).QuantizeFrame(image.Frames[0])) + using (IQuantizedFrame result = quantizer.CreateFrameQuantizer(config).QuantizeFrame(image.Frames[0])) { Assert.Equal(1, result.Palette.Length); Assert.Equal(1, result.GetPixelSpan().Length); @@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Tests.Quantization var quantizer = new WuQuantizer(false); using (var image = new Image(config, 1, 1, default(Rgba32))) - using (QuantizedFrame result = quantizer.CreateFrameQuantizer(config).QuantizeFrame(image.Frames[0])) + using (IQuantizedFrame result = quantizer.CreateFrameQuantizer(config).QuantizeFrame(image.Frames[0])) { Assert.Equal(1, result.Palette.Length); Assert.Equal(1, result.GetPixelSpan().Length); @@ -75,7 +75,7 @@ namespace SixLabors.ImageSharp.Tests.Quantization Configuration config = Configuration.Default; var quantizer = new WuQuantizer(false); using (IFrameQuantizer frameQuantizer = quantizer.CreateFrameQuantizer(config)) - using (QuantizedFrame result = frameQuantizer.QuantizeFrame(image.Frames[0])) + using (IQuantizedFrame result = frameQuantizer.QuantizeFrame(image.Frames[0])) { Assert.Equal(256, result.Palette.Length); Assert.Equal(256, result.GetPixelSpan().Length); @@ -113,7 +113,7 @@ namespace SixLabors.ImageSharp.Tests.Quantization Configuration config = Configuration.Default; var quantizer = new WuQuantizer(false); using (IFrameQuantizer frameQuantizer = quantizer.CreateFrameQuantizer(config)) - using (QuantizedFrame result = frameQuantizer.QuantizeFrame(image.Frames[0])) + using (IQuantizedFrame result = frameQuantizer.QuantizeFrame(image.Frames[0])) { Assert.Equal(48, result.Palette.Length); } @@ -142,7 +142,7 @@ namespace SixLabors.ImageSharp.Tests.Quantization var quantizer = new WuQuantizer(false); using (IFrameQuantizer frameQuantizer = quantizer.CreateFrameQuantizer(config)) - using (QuantizedFrame result = frameQuantizer.QuantizeFrame(image.Frames[0])) + using (IQuantizedFrame result = frameQuantizer.QuantizeFrame(image.Frames[0])) { Assert.Equal(4 * 8, result.Palette.Length); Assert.Equal(256, result.GetPixelSpan().Length); From 2cfdc9fb6ec64c9d9db9298e51587e1e40f82265 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 11 May 2019 00:33:00 +0200 Subject: [PATCH 162/223] move all extension methods under a subfolder (non-namespace providing) --- src/ImageSharp/ImageSharp.csproj.DotSettings | 1 + .../Processing/{ => Extensions}/AutoOrientExtensions.cs | 0 .../Processing/{ => Extensions}/BackgroundColorExtensions.cs | 0 .../Processing/{ => Extensions}/BinaryDiffuseExtensions.cs | 0 .../Processing/{ => Extensions}/BinaryDitherExtensions.cs | 0 .../Processing/{ => Extensions}/BinaryThresholdExtensions.cs | 0 .../Processing/{ => Extensions}/BlackWhiteExtensions.cs | 0 src/ImageSharp/Processing/{ => Extensions}/BoxBlurExtensions.cs | 0 .../Processing/{ => Extensions}/BrightnessExtensions.cs | 0 .../Processing/{ => Extensions}/ColorBlindnessExtensions.cs | 0 .../Processing/{ => Extensions}/ContrastExtensions.cs | 0 src/ImageSharp/Processing/{ => Extensions}/CropExtensions.cs | 0 .../Processing/{ => Extensions}/DetectEdgesExtensions.cs | 0 src/ImageSharp/Processing/{ => Extensions}/DiffuseExtensions.cs | 0 src/ImageSharp/Processing/{ => Extensions}/DitherExtensions.cs | 0 .../Processing/{ => Extensions}/EntropyCropExtensions.cs | 0 src/ImageSharp/Processing/{ => Extensions}/FilterExtensions.cs | 0 src/ImageSharp/Processing/{ => Extensions}/FlipExtensions.cs | 0 .../Processing/{ => Extensions}/GaussianBlurExtensions.cs | 0 .../Processing/{ => Extensions}/GaussianSharpenExtensions.cs | 0 src/ImageSharp/Processing/{ => Extensions}/GlowExtensions.cs | 0 .../Processing/{ => Extensions}/GrayscaleExtensions.cs | 0 .../HistogramEqualizationExtensions.cs} | 2 +- src/ImageSharp/Processing/{ => Extensions}/HueExtensions.cs | 0 src/ImageSharp/Processing/{ => Extensions}/InvertExtensions.cs | 0 .../Processing/{ => Extensions}/KodachromeExtensions.cs | 0 .../Processing/{ => Extensions}/LomographExtensions.cs | 0 .../Processing/{ => Extensions}/OilPaintExtensions.cs | 0 src/ImageSharp/Processing/{ => Extensions}/OpacityExtensions.cs | 0 src/ImageSharp/Processing/{ => Extensions}/PadExtensions.cs | 0 .../Processing/{ => Extensions}/PixelateExtensions.cs | 0 .../Processing/{ => Extensions}/PolaroidExtensions.cs | 0 .../Processing/{ => Extensions}/ProcessingExtensions.cs | 0 .../Processing/{ => Extensions}/QuantizeExtensions.cs | 0 src/ImageSharp/Processing/{ => Extensions}/ResizeExtensions.cs | 0 src/ImageSharp/Processing/{ => Extensions}/RotateExtensions.cs | 0 .../Processing/{ => Extensions}/RotateFlipExtensions.cs | 0 .../Processing/{ => Extensions}/SaturateExtensions.cs | 0 src/ImageSharp/Processing/{ => Extensions}/SepiaExtensions.cs | 0 src/ImageSharp/Processing/{ => Extensions}/SkewExtensions.cs | 0 .../Processing/{ => Extensions}/TransformExtensions.cs | 0 .../Processing/{ => Extensions}/VignetteExtensions.cs | 0 42 files changed, 2 insertions(+), 1 deletion(-) rename src/ImageSharp/Processing/{ => Extensions}/AutoOrientExtensions.cs (100%) rename src/ImageSharp/Processing/{ => Extensions}/BackgroundColorExtensions.cs (100%) rename src/ImageSharp/Processing/{ => Extensions}/BinaryDiffuseExtensions.cs (100%) rename src/ImageSharp/Processing/{ => Extensions}/BinaryDitherExtensions.cs (100%) rename src/ImageSharp/Processing/{ => Extensions}/BinaryThresholdExtensions.cs (100%) rename src/ImageSharp/Processing/{ => Extensions}/BlackWhiteExtensions.cs (100%) rename src/ImageSharp/Processing/{ => Extensions}/BoxBlurExtensions.cs (100%) rename src/ImageSharp/Processing/{ => Extensions}/BrightnessExtensions.cs (100%) rename src/ImageSharp/Processing/{ => Extensions}/ColorBlindnessExtensions.cs (100%) rename src/ImageSharp/Processing/{ => Extensions}/ContrastExtensions.cs (100%) rename src/ImageSharp/Processing/{ => Extensions}/CropExtensions.cs (100%) rename src/ImageSharp/Processing/{ => Extensions}/DetectEdgesExtensions.cs (100%) rename src/ImageSharp/Processing/{ => Extensions}/DiffuseExtensions.cs (100%) rename src/ImageSharp/Processing/{ => Extensions}/DitherExtensions.cs (100%) rename src/ImageSharp/Processing/{ => Extensions}/EntropyCropExtensions.cs (100%) rename src/ImageSharp/Processing/{ => Extensions}/FilterExtensions.cs (100%) rename src/ImageSharp/Processing/{ => Extensions}/FlipExtensions.cs (100%) rename src/ImageSharp/Processing/{ => Extensions}/GaussianBlurExtensions.cs (100%) rename src/ImageSharp/Processing/{ => Extensions}/GaussianSharpenExtensions.cs (100%) rename src/ImageSharp/Processing/{ => Extensions}/GlowExtensions.cs (100%) rename src/ImageSharp/Processing/{ => Extensions}/GrayscaleExtensions.cs (100%) rename src/ImageSharp/Processing/{HistogramEqualizationExtension.cs => Extensions/HistogramEqualizationExtensions.cs} (96%) rename src/ImageSharp/Processing/{ => Extensions}/HueExtensions.cs (100%) rename src/ImageSharp/Processing/{ => Extensions}/InvertExtensions.cs (100%) rename src/ImageSharp/Processing/{ => Extensions}/KodachromeExtensions.cs (100%) rename src/ImageSharp/Processing/{ => Extensions}/LomographExtensions.cs (100%) rename src/ImageSharp/Processing/{ => Extensions}/OilPaintExtensions.cs (100%) rename src/ImageSharp/Processing/{ => Extensions}/OpacityExtensions.cs (100%) rename src/ImageSharp/Processing/{ => Extensions}/PadExtensions.cs (100%) rename src/ImageSharp/Processing/{ => Extensions}/PixelateExtensions.cs (100%) rename src/ImageSharp/Processing/{ => Extensions}/PolaroidExtensions.cs (100%) rename src/ImageSharp/Processing/{ => Extensions}/ProcessingExtensions.cs (100%) rename src/ImageSharp/Processing/{ => Extensions}/QuantizeExtensions.cs (100%) rename src/ImageSharp/Processing/{ => Extensions}/ResizeExtensions.cs (100%) rename src/ImageSharp/Processing/{ => Extensions}/RotateExtensions.cs (100%) rename src/ImageSharp/Processing/{ => Extensions}/RotateFlipExtensions.cs (100%) rename src/ImageSharp/Processing/{ => Extensions}/SaturateExtensions.cs (100%) rename src/ImageSharp/Processing/{ => Extensions}/SepiaExtensions.cs (100%) rename src/ImageSharp/Processing/{ => Extensions}/SkewExtensions.cs (100%) rename src/ImageSharp/Processing/{ => Extensions}/TransformExtensions.cs (100%) rename src/ImageSharp/Processing/{ => Extensions}/VignetteExtensions.cs (100%) diff --git a/src/ImageSharp/ImageSharp.csproj.DotSettings b/src/ImageSharp/ImageSharp.csproj.DotSettings index e43e92ddce..018ca75cdf 100644 --- a/src/ImageSharp/ImageSharp.csproj.DotSettings +++ b/src/ImageSharp/ImageSharp.csproj.DotSettings @@ -7,6 +7,7 @@ True True True + True True True True \ No newline at end of file diff --git a/src/ImageSharp/Processing/AutoOrientExtensions.cs b/src/ImageSharp/Processing/Extensions/AutoOrientExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/AutoOrientExtensions.cs rename to src/ImageSharp/Processing/Extensions/AutoOrientExtensions.cs diff --git a/src/ImageSharp/Processing/BackgroundColorExtensions.cs b/src/ImageSharp/Processing/Extensions/BackgroundColorExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/BackgroundColorExtensions.cs rename to src/ImageSharp/Processing/Extensions/BackgroundColorExtensions.cs diff --git a/src/ImageSharp/Processing/BinaryDiffuseExtensions.cs b/src/ImageSharp/Processing/Extensions/BinaryDiffuseExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/BinaryDiffuseExtensions.cs rename to src/ImageSharp/Processing/Extensions/BinaryDiffuseExtensions.cs diff --git a/src/ImageSharp/Processing/BinaryDitherExtensions.cs b/src/ImageSharp/Processing/Extensions/BinaryDitherExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/BinaryDitherExtensions.cs rename to src/ImageSharp/Processing/Extensions/BinaryDitherExtensions.cs diff --git a/src/ImageSharp/Processing/BinaryThresholdExtensions.cs b/src/ImageSharp/Processing/Extensions/BinaryThresholdExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/BinaryThresholdExtensions.cs rename to src/ImageSharp/Processing/Extensions/BinaryThresholdExtensions.cs diff --git a/src/ImageSharp/Processing/BlackWhiteExtensions.cs b/src/ImageSharp/Processing/Extensions/BlackWhiteExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/BlackWhiteExtensions.cs rename to src/ImageSharp/Processing/Extensions/BlackWhiteExtensions.cs diff --git a/src/ImageSharp/Processing/BoxBlurExtensions.cs b/src/ImageSharp/Processing/Extensions/BoxBlurExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/BoxBlurExtensions.cs rename to src/ImageSharp/Processing/Extensions/BoxBlurExtensions.cs diff --git a/src/ImageSharp/Processing/BrightnessExtensions.cs b/src/ImageSharp/Processing/Extensions/BrightnessExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/BrightnessExtensions.cs rename to src/ImageSharp/Processing/Extensions/BrightnessExtensions.cs diff --git a/src/ImageSharp/Processing/ColorBlindnessExtensions.cs b/src/ImageSharp/Processing/Extensions/ColorBlindnessExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/ColorBlindnessExtensions.cs rename to src/ImageSharp/Processing/Extensions/ColorBlindnessExtensions.cs diff --git a/src/ImageSharp/Processing/ContrastExtensions.cs b/src/ImageSharp/Processing/Extensions/ContrastExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/ContrastExtensions.cs rename to src/ImageSharp/Processing/Extensions/ContrastExtensions.cs diff --git a/src/ImageSharp/Processing/CropExtensions.cs b/src/ImageSharp/Processing/Extensions/CropExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/CropExtensions.cs rename to src/ImageSharp/Processing/Extensions/CropExtensions.cs diff --git a/src/ImageSharp/Processing/DetectEdgesExtensions.cs b/src/ImageSharp/Processing/Extensions/DetectEdgesExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/DetectEdgesExtensions.cs rename to src/ImageSharp/Processing/Extensions/DetectEdgesExtensions.cs diff --git a/src/ImageSharp/Processing/DiffuseExtensions.cs b/src/ImageSharp/Processing/Extensions/DiffuseExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/DiffuseExtensions.cs rename to src/ImageSharp/Processing/Extensions/DiffuseExtensions.cs diff --git a/src/ImageSharp/Processing/DitherExtensions.cs b/src/ImageSharp/Processing/Extensions/DitherExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/DitherExtensions.cs rename to src/ImageSharp/Processing/Extensions/DitherExtensions.cs diff --git a/src/ImageSharp/Processing/EntropyCropExtensions.cs b/src/ImageSharp/Processing/Extensions/EntropyCropExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/EntropyCropExtensions.cs rename to src/ImageSharp/Processing/Extensions/EntropyCropExtensions.cs diff --git a/src/ImageSharp/Processing/FilterExtensions.cs b/src/ImageSharp/Processing/Extensions/FilterExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/FilterExtensions.cs rename to src/ImageSharp/Processing/Extensions/FilterExtensions.cs diff --git a/src/ImageSharp/Processing/FlipExtensions.cs b/src/ImageSharp/Processing/Extensions/FlipExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/FlipExtensions.cs rename to src/ImageSharp/Processing/Extensions/FlipExtensions.cs diff --git a/src/ImageSharp/Processing/GaussianBlurExtensions.cs b/src/ImageSharp/Processing/Extensions/GaussianBlurExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/GaussianBlurExtensions.cs rename to src/ImageSharp/Processing/Extensions/GaussianBlurExtensions.cs diff --git a/src/ImageSharp/Processing/GaussianSharpenExtensions.cs b/src/ImageSharp/Processing/Extensions/GaussianSharpenExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/GaussianSharpenExtensions.cs rename to src/ImageSharp/Processing/Extensions/GaussianSharpenExtensions.cs diff --git a/src/ImageSharp/Processing/GlowExtensions.cs b/src/ImageSharp/Processing/Extensions/GlowExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/GlowExtensions.cs rename to src/ImageSharp/Processing/Extensions/GlowExtensions.cs diff --git a/src/ImageSharp/Processing/GrayscaleExtensions.cs b/src/ImageSharp/Processing/Extensions/GrayscaleExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/GrayscaleExtensions.cs rename to src/ImageSharp/Processing/Extensions/GrayscaleExtensions.cs diff --git a/src/ImageSharp/Processing/HistogramEqualizationExtension.cs b/src/ImageSharp/Processing/Extensions/HistogramEqualizationExtensions.cs similarity index 96% rename from src/ImageSharp/Processing/HistogramEqualizationExtension.cs rename to src/ImageSharp/Processing/Extensions/HistogramEqualizationExtensions.cs index 01c14fc093..72962a3f9f 100644 --- a/src/ImageSharp/Processing/HistogramEqualizationExtension.cs +++ b/src/ImageSharp/Processing/Extensions/HistogramEqualizationExtensions.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing /// /// Defines extension that allow the adjustment of the contrast of an image via its histogram. /// - public static class HistogramEqualizationExtension + public static class HistogramEqualizationExtensions { /// /// Equalizes the histogram of an image to increases the contrast. diff --git a/src/ImageSharp/Processing/HueExtensions.cs b/src/ImageSharp/Processing/Extensions/HueExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/HueExtensions.cs rename to src/ImageSharp/Processing/Extensions/HueExtensions.cs diff --git a/src/ImageSharp/Processing/InvertExtensions.cs b/src/ImageSharp/Processing/Extensions/InvertExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/InvertExtensions.cs rename to src/ImageSharp/Processing/Extensions/InvertExtensions.cs diff --git a/src/ImageSharp/Processing/KodachromeExtensions.cs b/src/ImageSharp/Processing/Extensions/KodachromeExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/KodachromeExtensions.cs rename to src/ImageSharp/Processing/Extensions/KodachromeExtensions.cs diff --git a/src/ImageSharp/Processing/LomographExtensions.cs b/src/ImageSharp/Processing/Extensions/LomographExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/LomographExtensions.cs rename to src/ImageSharp/Processing/Extensions/LomographExtensions.cs diff --git a/src/ImageSharp/Processing/OilPaintExtensions.cs b/src/ImageSharp/Processing/Extensions/OilPaintExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/OilPaintExtensions.cs rename to src/ImageSharp/Processing/Extensions/OilPaintExtensions.cs diff --git a/src/ImageSharp/Processing/OpacityExtensions.cs b/src/ImageSharp/Processing/Extensions/OpacityExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/OpacityExtensions.cs rename to src/ImageSharp/Processing/Extensions/OpacityExtensions.cs diff --git a/src/ImageSharp/Processing/PadExtensions.cs b/src/ImageSharp/Processing/Extensions/PadExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/PadExtensions.cs rename to src/ImageSharp/Processing/Extensions/PadExtensions.cs diff --git a/src/ImageSharp/Processing/PixelateExtensions.cs b/src/ImageSharp/Processing/Extensions/PixelateExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/PixelateExtensions.cs rename to src/ImageSharp/Processing/Extensions/PixelateExtensions.cs diff --git a/src/ImageSharp/Processing/PolaroidExtensions.cs b/src/ImageSharp/Processing/Extensions/PolaroidExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/PolaroidExtensions.cs rename to src/ImageSharp/Processing/Extensions/PolaroidExtensions.cs diff --git a/src/ImageSharp/Processing/ProcessingExtensions.cs b/src/ImageSharp/Processing/Extensions/ProcessingExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/ProcessingExtensions.cs rename to src/ImageSharp/Processing/Extensions/ProcessingExtensions.cs diff --git a/src/ImageSharp/Processing/QuantizeExtensions.cs b/src/ImageSharp/Processing/Extensions/QuantizeExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/QuantizeExtensions.cs rename to src/ImageSharp/Processing/Extensions/QuantizeExtensions.cs diff --git a/src/ImageSharp/Processing/ResizeExtensions.cs b/src/ImageSharp/Processing/Extensions/ResizeExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/ResizeExtensions.cs rename to src/ImageSharp/Processing/Extensions/ResizeExtensions.cs diff --git a/src/ImageSharp/Processing/RotateExtensions.cs b/src/ImageSharp/Processing/Extensions/RotateExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/RotateExtensions.cs rename to src/ImageSharp/Processing/Extensions/RotateExtensions.cs diff --git a/src/ImageSharp/Processing/RotateFlipExtensions.cs b/src/ImageSharp/Processing/Extensions/RotateFlipExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/RotateFlipExtensions.cs rename to src/ImageSharp/Processing/Extensions/RotateFlipExtensions.cs diff --git a/src/ImageSharp/Processing/SaturateExtensions.cs b/src/ImageSharp/Processing/Extensions/SaturateExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/SaturateExtensions.cs rename to src/ImageSharp/Processing/Extensions/SaturateExtensions.cs diff --git a/src/ImageSharp/Processing/SepiaExtensions.cs b/src/ImageSharp/Processing/Extensions/SepiaExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/SepiaExtensions.cs rename to src/ImageSharp/Processing/Extensions/SepiaExtensions.cs diff --git a/src/ImageSharp/Processing/SkewExtensions.cs b/src/ImageSharp/Processing/Extensions/SkewExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/SkewExtensions.cs rename to src/ImageSharp/Processing/Extensions/SkewExtensions.cs diff --git a/src/ImageSharp/Processing/TransformExtensions.cs b/src/ImageSharp/Processing/Extensions/TransformExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/TransformExtensions.cs rename to src/ImageSharp/Processing/Extensions/TransformExtensions.cs diff --git a/src/ImageSharp/Processing/VignetteExtensions.cs b/src/ImageSharp/Processing/Extensions/VignetteExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/VignetteExtensions.cs rename to src/ImageSharp/Processing/Extensions/VignetteExtensions.cs From 706211d144232ecf4c94aa2a2140ce3eb6c7b6e0 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 11 May 2019 13:45:26 +0200 Subject: [PATCH 163/223] re-enable all target frameworks --- src/ImageSharp.Drawing/ImageSharp.Drawing.csproj | 3 +-- src/ImageSharp/ImageSharp.csproj | 3 +-- tests/ImageSharp.Tests/ImageSharp.Tests.csproj | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj index fdeb27a770..915915f6cc 100644 --- a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj +++ b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj @@ -11,8 +11,7 @@ $(packageversion) 0.0.1 - - netstandard2.0 + netcoreapp2.1;netstandard1.3;netstandard2.0 7.3 true diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index 4abe2f57a2..d734648f41 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -11,8 +11,7 @@ $(packageversion) 0.0.1 - - netstandard2.0 + netcoreapp2.1;netstandard1.3;netstandard2.0;net472 true true diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index efdf89964c..0b727f30ce 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -1,7 +1,6 @@  - - netcoreapp2.1 + netcoreapp2.1;net462;net472 True latest full From 8618a05f44f1a54a72b3ac2b81f9a5cd94066201 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sat, 11 May 2019 13:52:18 +0200 Subject: [PATCH 164/223] Using tolerant comparer for the Bit8Gs image --- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 3 +- .../Formats/Bmp/BmpEncoderTests.cs | 37 ++++++++++++++++--- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 9d9c7b624a..0cbc4fca1b 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -1022,7 +1022,8 @@ namespace SixLabors.ImageSharp.Formats.Bmp this.bmpMetadata.InfoHeaderType = infoHeaderType; // We can only encode at these bit rates so far. - if (bitsPerPixel.Equals((short)BmpBitsPerPixel.Pixel16) + if (bitsPerPixel.Equals((short)BmpBitsPerPixel.Pixel8) + || bitsPerPixel.Equals((short)BmpBitsPerPixel.Pixel16) || bitsPerPixel.Equals((short)BmpBitsPerPixel.Pixel24) || bitsPerPixel.Equals((short)BmpBitsPerPixel.Pixel32)) { diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs index f3d6e0fa8e..1d489e69dc 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs @@ -3,12 +3,11 @@ using System.IO; -using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using Xunit; using Xunit.Abstractions; @@ -128,11 +127,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp [Theory] // WinBmpv3 is a 24 bits per pixel image [WithFile(WinBmpv3, PixelTypes.Rgb24, BmpBitsPerPixel.Pixel24)] + [WithFile(F, PixelTypes.Rgb24, BmpBitsPerPixel.Pixel24)] public void Encode_24Bit_WithV3Header_Works(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) where TPixel : struct, IPixel => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: false); [Theory] [WithFile(WinBmpv3, PixelTypes.Rgb24, BmpBitsPerPixel.Pixel24)] + [WithFile(F, PixelTypes.Rgb24, BmpBitsPerPixel.Pixel24)] public void Encode_24Bit_WithV4Header_Works(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) where TPixel : struct, IPixel => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: true); @@ -150,24 +151,48 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp where TPixel : struct, IPixel => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: true); [Theory] + [WithFile(WinBmpv5, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel8)] [WithFile(Bit8Palette4, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel8)] - [WithFile(Bit8Gs, PixelTypes.Gray8, BmpBitsPerPixel.Pixel8)] public void Encode_8Bit_WithV3Header_Works(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) where TPixel : struct, IPixel => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: false); [Theory] + [WithFile(WinBmpv5, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel8)] [WithFile(Bit8Palette4, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel8)] - [WithFile(Bit8Gs, PixelTypes.Gray8, BmpBitsPerPixel.Pixel8)] public void Encode_8Bit_WithV4Header_Works(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) where TPixel : struct, IPixel => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: true); + [Theory] + [WithFile(Bit8Gs, PixelTypes.Gray8, BmpBitsPerPixel.Pixel8)] + public void Encode_8BitGray_WithV3Header_Works(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) + where TPixel : struct, IPixel => + TestBmpEncoderCore( + provider, + bitsPerPixel, + supportTransparency: false, + ImageComparer.TolerantPercentage(0.01f)); + + [Theory] + [WithFile(Bit8Gs, PixelTypes.Gray8, BmpBitsPerPixel.Pixel8)] + public void Encode_8BitGray_WithV4Header_Works(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) + where TPixel : struct, IPixel => + TestBmpEncoderCore( + provider, + bitsPerPixel, + supportTransparency: true, + ImageComparer.TolerantPercentage(0.01f)); + [Theory] [WithFile(TestImages.Png.GrayAlpha2BitInterlaced, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel32)] [WithFile(Bit32Rgba, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel32)] public void Encode_PreservesAlpha(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) where TPixel : struct, IPixel => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: true); - private static void TestBmpEncoderCore(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel, bool supportTransparency = true) + private static void TestBmpEncoderCore( + TestImageProvider provider, + BmpBitsPerPixel bitsPerPixel, + bool supportTransparency = true, + ImageComparer customComparer = null) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) @@ -181,7 +206,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp var encoder = new BmpEncoder { BitsPerPixel = bitsPerPixel, SupportTransparency = supportTransparency }; // Does DebugSave & load reference CompareToReferenceInput(): - image.VerifyEncoder(provider, "bmp", bitsPerPixel, encoder); + image.VerifyEncoder(provider, "bmp", bitsPerPixel, encoder, customComparer); } } } From 8425cae737ed8b9f29f8efc3ee94ada159fe5920 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 11 May 2019 13:57:21 +0200 Subject: [PATCH 165/223] More docs for Color, correct naming in ColorBuilder --- src/ImageSharp/Color/Color.cs | 8 +- .../PixelFormats/ColorBuilder{TPixel}.cs | 4 +- .../PixelFormats/NamedColors{TPixel}.cs | 284 +++++++++--------- 3 files changed, 151 insertions(+), 145 deletions(-) diff --git a/src/ImageSharp/Color/Color.cs b/src/ImageSharp/Color/Color.cs index 94b5dde2ee..f2f8578d83 100644 --- a/src/ImageSharp/Color/Color.cs +++ b/src/ImageSharp/Color/Color.cs @@ -12,8 +12,14 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp { /// - /// Represents a color value that is convertible to all implementations. + /// Represents a color value that is convertible to any type. /// + /// + /// The internal representation and layout of this structure is hidden by intention. + /// It's not serializable, and it should not be considered as part of a contract. + /// Unlike System.Drawing.Color, has to be converted to a specific pixel value + /// to query the color components. + /// public readonly partial struct Color : IEquatable { private readonly Rgba64 data; diff --git a/src/ImageSharp/PixelFormats/ColorBuilder{TPixel}.cs b/src/ImageSharp/PixelFormats/ColorBuilder{TPixel}.cs index 2572b32933..c1fb13e4da 100644 --- a/src/ImageSharp/PixelFormats/ColorBuilder{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/ColorBuilder{TPixel}.cs @@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// The green intensity. /// The blue intensity. /// Returns a that represents the color defined by the provided RGB values with 100% opacity. - public static TPixel FromRGB(byte red, byte green, byte blue) => FromRGBA(red, green, blue, 255); + public static TPixel FromRgb(byte red, byte green, byte blue) => FromRgba(red, green, blue, 255); /// /// Creates a new representation from standard RGBA bytes. @@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// The blue intensity. /// The alpha intensity. /// Returns a that represents the color defined by the provided RGBA values. - public static TPixel FromRGBA(byte red, byte green, byte blue, byte alpha) + public static TPixel FromRgba(byte red, byte green, byte blue, byte alpha) { TPixel color = default; color.FromRgba32(new Rgba32(red, green, blue, alpha)); diff --git a/src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs b/src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs index 7e093de042..2138e2e2f7 100644 --- a/src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs @@ -22,712 +22,712 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// Represents a matching the W3C definition that has an hex value of #F0F8FF. /// - public static readonly TPixel AliceBlue = ColorBuilder.FromRGBA(240, 248, 255, 255); + public static readonly TPixel AliceBlue = ColorBuilder.FromRgba(240, 248, 255, 255); /// /// Represents a matching the W3C definition that has an hex value of #FAEBD7. /// - public static readonly TPixel AntiqueWhite = ColorBuilder.FromRGBA(250, 235, 215, 255); + public static readonly TPixel AntiqueWhite = ColorBuilder.FromRgba(250, 235, 215, 255); /// /// Represents a matching the W3C definition that has an hex value of #00FFFF. /// - public static readonly TPixel Aqua = ColorBuilder.FromRGBA(0, 255, 255, 255); + public static readonly TPixel Aqua = ColorBuilder.FromRgba(0, 255, 255, 255); /// /// Represents a matching the W3C definition that has an hex value of #7FFFD4. /// - public static readonly TPixel Aquamarine = ColorBuilder.FromRGBA(127, 255, 212, 255); + public static readonly TPixel Aquamarine = ColorBuilder.FromRgba(127, 255, 212, 255); /// /// Represents a matching the W3C definition that has an hex value of #F0FFFF. /// - public static readonly TPixel Azure = ColorBuilder.FromRGBA(240, 255, 255, 255); + public static readonly TPixel Azure = ColorBuilder.FromRgba(240, 255, 255, 255); /// /// Represents a matching the W3C definition that has an hex value of #F5F5DC. /// - public static readonly TPixel Beige = ColorBuilder.FromRGBA(245, 245, 220, 255); + public static readonly TPixel Beige = ColorBuilder.FromRgba(245, 245, 220, 255); /// /// Represents a matching the W3C definition that has an hex value of #FFE4C4. /// - public static readonly TPixel Bisque = ColorBuilder.FromRGBA(255, 228, 196, 255); + public static readonly TPixel Bisque = ColorBuilder.FromRgba(255, 228, 196, 255); /// /// Represents a matching the W3C definition that has an hex value of #000000. /// - public static readonly TPixel Black = ColorBuilder.FromRGBA(0, 0, 0, 255); + public static readonly TPixel Black = ColorBuilder.FromRgba(0, 0, 0, 255); /// /// Represents a matching the W3C definition that has an hex value of #FFEBCD. /// - public static readonly TPixel BlanchedAlmond = ColorBuilder.FromRGBA(255, 235, 205, 255); + public static readonly TPixel BlanchedAlmond = ColorBuilder.FromRgba(255, 235, 205, 255); /// /// Represents a matching the W3C definition that has an hex value of #0000FF. /// - public static readonly TPixel Blue = ColorBuilder.FromRGBA(0, 0, 255, 255); + public static readonly TPixel Blue = ColorBuilder.FromRgba(0, 0, 255, 255); /// /// Represents a matching the W3C definition that has an hex value of #8A2BE2. /// - public static readonly TPixel BlueViolet = ColorBuilder.FromRGBA(138, 43, 226, 255); + public static readonly TPixel BlueViolet = ColorBuilder.FromRgba(138, 43, 226, 255); /// /// Represents a matching the W3C definition that has an hex value of #A52A2A. /// - public static readonly TPixel Brown = ColorBuilder.FromRGBA(165, 42, 42, 255); + public static readonly TPixel Brown = ColorBuilder.FromRgba(165, 42, 42, 255); /// /// Represents a matching the W3C definition that has an hex value of #DEB887. /// - public static readonly TPixel BurlyWood = ColorBuilder.FromRGBA(222, 184, 135, 255); + public static readonly TPixel BurlyWood = ColorBuilder.FromRgba(222, 184, 135, 255); /// /// Represents a matching the W3C definition that has an hex value of #5F9EA0. /// - public static readonly TPixel CadetBlue = ColorBuilder.FromRGBA(95, 158, 160, 255); + public static readonly TPixel CadetBlue = ColorBuilder.FromRgba(95, 158, 160, 255); /// /// Represents a matching the W3C definition that has an hex value of #7FFF00. /// - public static readonly TPixel Chartreuse = ColorBuilder.FromRGBA(127, 255, 0, 255); + public static readonly TPixel Chartreuse = ColorBuilder.FromRgba(127, 255, 0, 255); /// /// Represents a matching the W3C definition that has an hex value of #D2691E. /// - public static readonly TPixel Chocolate = ColorBuilder.FromRGBA(210, 105, 30, 255); + public static readonly TPixel Chocolate = ColorBuilder.FromRgba(210, 105, 30, 255); /// /// Represents a matching the W3C definition that has an hex value of #FF7F50. /// - public static readonly TPixel Coral = ColorBuilder.FromRGBA(255, 127, 80, 255); + public static readonly TPixel Coral = ColorBuilder.FromRgba(255, 127, 80, 255); /// /// Represents a matching the W3C definition that has an hex value of #6495ED. /// - public static readonly TPixel CornflowerBlue = ColorBuilder.FromRGBA(100, 149, 237, 255); + public static readonly TPixel CornflowerBlue = ColorBuilder.FromRgba(100, 149, 237, 255); /// /// Represents a matching the W3C definition that has an hex value of #FFF8DC. /// - public static readonly TPixel Cornsilk = ColorBuilder.FromRGBA(255, 248, 220, 255); + public static readonly TPixel Cornsilk = ColorBuilder.FromRgba(255, 248, 220, 255); /// /// Represents a matching the W3C definition that has an hex value of #DC143C. /// - public static readonly TPixel Crimson = ColorBuilder.FromRGBA(220, 20, 60, 255); + public static readonly TPixel Crimson = ColorBuilder.FromRgba(220, 20, 60, 255); /// /// Represents a matching the W3C definition that has an hex value of #00FFFF. /// - public static readonly TPixel Cyan = ColorBuilder.FromRGBA(0, 255, 255, 255); + public static readonly TPixel Cyan = ColorBuilder.FromRgba(0, 255, 255, 255); /// /// Represents a matching the W3C definition that has an hex value of #00008B. /// - public static readonly TPixel DarkBlue = ColorBuilder.FromRGBA(0, 0, 139, 255); + public static readonly TPixel DarkBlue = ColorBuilder.FromRgba(0, 0, 139, 255); /// /// Represents a matching the W3C definition that has an hex value of #008B8B. /// - public static readonly TPixel DarkCyan = ColorBuilder.FromRGBA(0, 139, 139, 255); + public static readonly TPixel DarkCyan = ColorBuilder.FromRgba(0, 139, 139, 255); /// /// Represents a matching the W3C definition that has an hex value of #B8860B. /// - public static readonly TPixel DarkGoldenrod = ColorBuilder.FromRGBA(184, 134, 11, 255); + public static readonly TPixel DarkGoldenrod = ColorBuilder.FromRgba(184, 134, 11, 255); /// /// Represents a matching the W3C definition that has an hex value of #A9A9A9. /// - public static readonly TPixel DarkGray = ColorBuilder.FromRGBA(169, 169, 169, 255); + public static readonly TPixel DarkGray = ColorBuilder.FromRgba(169, 169, 169, 255); /// /// Represents a matching the W3C definition that has an hex value of #006400. /// - public static readonly TPixel DarkGreen = ColorBuilder.FromRGBA(0, 100, 0, 255); + public static readonly TPixel DarkGreen = ColorBuilder.FromRgba(0, 100, 0, 255); /// /// Represents a matching the W3C definition that has an hex value of #BDB76B. /// - public static readonly TPixel DarkKhaki = ColorBuilder.FromRGBA(189, 183, 107, 255); + public static readonly TPixel DarkKhaki = ColorBuilder.FromRgba(189, 183, 107, 255); /// /// Represents a matching the W3C definition that has an hex value of #8B008B. /// - public static readonly TPixel DarkMagenta = ColorBuilder.FromRGBA(139, 0, 139, 255); + public static readonly TPixel DarkMagenta = ColorBuilder.FromRgba(139, 0, 139, 255); /// /// Represents a matching the W3C definition that has an hex value of #556B2F. /// - public static readonly TPixel DarkOliveGreen = ColorBuilder.FromRGBA(85, 107, 47, 255); + public static readonly TPixel DarkOliveGreen = ColorBuilder.FromRgba(85, 107, 47, 255); /// /// Represents a matching the W3C definition that has an hex value of #FF8C00. /// - public static readonly TPixel DarkOrange = ColorBuilder.FromRGBA(255, 140, 0, 255); + public static readonly TPixel DarkOrange = ColorBuilder.FromRgba(255, 140, 0, 255); /// /// Represents a matching the W3C definition that has an hex value of #9932CC. /// - public static readonly TPixel DarkOrchid = ColorBuilder.FromRGBA(153, 50, 204, 255); + public static readonly TPixel DarkOrchid = ColorBuilder.FromRgba(153, 50, 204, 255); /// /// Represents a matching the W3C definition that has an hex value of #8B0000. /// - public static readonly TPixel DarkRed = ColorBuilder.FromRGBA(139, 0, 0, 255); + public static readonly TPixel DarkRed = ColorBuilder.FromRgba(139, 0, 0, 255); /// /// Represents a matching the W3C definition that has an hex value of #E9967A. /// - public static readonly TPixel DarkSalmon = ColorBuilder.FromRGBA(233, 150, 122, 255); + public static readonly TPixel DarkSalmon = ColorBuilder.FromRgba(233, 150, 122, 255); /// /// Represents a matching the W3C definition that has an hex value of #8FBC8B. /// - public static readonly TPixel DarkSeaGreen = ColorBuilder.FromRGBA(143, 188, 139, 255); + public static readonly TPixel DarkSeaGreen = ColorBuilder.FromRgba(143, 188, 139, 255); /// /// Represents a matching the W3C definition that has an hex value of #483D8B. /// - public static readonly TPixel DarkSlateBlue = ColorBuilder.FromRGBA(72, 61, 139, 255); + public static readonly TPixel DarkSlateBlue = ColorBuilder.FromRgba(72, 61, 139, 255); /// /// Represents a matching the W3C definition that has an hex value of #2F4F4F. /// - public static readonly TPixel DarkSlateGray = ColorBuilder.FromRGBA(47, 79, 79, 255); + public static readonly TPixel DarkSlateGray = ColorBuilder.FromRgba(47, 79, 79, 255); /// /// Represents a matching the W3C definition that has an hex value of #00CED1. /// - public static readonly TPixel DarkTurquoise = ColorBuilder.FromRGBA(0, 206, 209, 255); + public static readonly TPixel DarkTurquoise = ColorBuilder.FromRgba(0, 206, 209, 255); /// /// Represents a matching the W3C definition that has an hex value of #9400D3. /// - public static readonly TPixel DarkViolet = ColorBuilder.FromRGBA(148, 0, 211, 255); + public static readonly TPixel DarkViolet = ColorBuilder.FromRgba(148, 0, 211, 255); /// /// Represents a matching the W3C definition that has an hex value of #FF1493. /// - public static readonly TPixel DeepPink = ColorBuilder.FromRGBA(255, 20, 147, 255); + public static readonly TPixel DeepPink = ColorBuilder.FromRgba(255, 20, 147, 255); /// /// Represents a matching the W3C definition that has an hex value of #00BFFF. /// - public static readonly TPixel DeepSkyBlue = ColorBuilder.FromRGBA(0, 191, 255, 255); + public static readonly TPixel DeepSkyBlue = ColorBuilder.FromRgba(0, 191, 255, 255); /// /// Represents a matching the W3C definition that has an hex value of #696969. /// - public static readonly TPixel DimGray = ColorBuilder.FromRGBA(105, 105, 105, 255); + public static readonly TPixel DimGray = ColorBuilder.FromRgba(105, 105, 105, 255); /// /// Represents a matching the W3C definition that has an hex value of #1E90FF. /// - public static readonly TPixel DodgerBlue = ColorBuilder.FromRGBA(30, 144, 255, 255); + public static readonly TPixel DodgerBlue = ColorBuilder.FromRgba(30, 144, 255, 255); /// /// Represents a matching the W3C definition that has an hex value of #B22222. /// - public static readonly TPixel Firebrick = ColorBuilder.FromRGBA(178, 34, 34, 255); + public static readonly TPixel Firebrick = ColorBuilder.FromRgba(178, 34, 34, 255); /// /// Represents a matching the W3C definition that has an hex value of #FFFAF0. /// - public static readonly TPixel FloralWhite = ColorBuilder.FromRGBA(255, 250, 240, 255); + public static readonly TPixel FloralWhite = ColorBuilder.FromRgba(255, 250, 240, 255); /// /// Represents a matching the W3C definition that has an hex value of #228B22. /// - public static readonly TPixel ForestGreen = ColorBuilder.FromRGBA(34, 139, 34, 255); + public static readonly TPixel ForestGreen = ColorBuilder.FromRgba(34, 139, 34, 255); /// /// Represents a matching the W3C definition that has an hex value of #FF00FF. /// - public static readonly TPixel Fuchsia = ColorBuilder.FromRGBA(255, 0, 255, 255); + public static readonly TPixel Fuchsia = ColorBuilder.FromRgba(255, 0, 255, 255); /// /// Represents a matching the W3C definition that has an hex value of #DCDCDC. /// - public static readonly TPixel Gainsboro = ColorBuilder.FromRGBA(220, 220, 220, 255); + public static readonly TPixel Gainsboro = ColorBuilder.FromRgba(220, 220, 220, 255); /// /// Represents a matching the W3C definition that has an hex value of #F8F8FF. /// - public static readonly TPixel GhostWhite = ColorBuilder.FromRGBA(248, 248, 255, 255); + public static readonly TPixel GhostWhite = ColorBuilder.FromRgba(248, 248, 255, 255); /// /// Represents a matching the W3C definition that has an hex value of #FFD700. /// - public static readonly TPixel Gold = ColorBuilder.FromRGBA(255, 215, 0, 255); + public static readonly TPixel Gold = ColorBuilder.FromRgba(255, 215, 0, 255); /// /// Represents a matching the W3C definition that has an hex value of #DAA520. /// - public static readonly TPixel Goldenrod = ColorBuilder.FromRGBA(218, 165, 32, 255); + public static readonly TPixel Goldenrod = ColorBuilder.FromRgba(218, 165, 32, 255); /// /// Represents a matching the W3C definition that has an hex value of #808080. /// - public static readonly TPixel Gray = ColorBuilder.FromRGBA(128, 128, 128, 255); + public static readonly TPixel Gray = ColorBuilder.FromRgba(128, 128, 128, 255); /// /// Represents a matching the W3C definition that has an hex value of #008000. /// - public static readonly TPixel Green = ColorBuilder.FromRGBA(0, 128, 0, 255); + public static readonly TPixel Green = ColorBuilder.FromRgba(0, 128, 0, 255); /// /// Represents a matching the W3C definition that has an hex value of #ADFF2F. /// - public static readonly TPixel GreenYellow = ColorBuilder.FromRGBA(173, 255, 47, 255); + public static readonly TPixel GreenYellow = ColorBuilder.FromRgba(173, 255, 47, 255); /// /// Represents a matching the W3C definition that has an hex value of #F0FFF0. /// - public static readonly TPixel Honeydew = ColorBuilder.FromRGBA(240, 255, 240, 255); + public static readonly TPixel Honeydew = ColorBuilder.FromRgba(240, 255, 240, 255); /// /// Represents a matching the W3C definition that has an hex value of #FF69B4. /// - public static readonly TPixel HotPink = ColorBuilder.FromRGBA(255, 105, 180, 255); + public static readonly TPixel HotPink = ColorBuilder.FromRgba(255, 105, 180, 255); /// /// Represents a matching the W3C definition that has an hex value of #CD5C5C. /// - public static readonly TPixel IndianRed = ColorBuilder.FromRGBA(205, 92, 92, 255); + public static readonly TPixel IndianRed = ColorBuilder.FromRgba(205, 92, 92, 255); /// /// Represents a matching the W3C definition that has an hex value of #4B0082. /// - public static readonly TPixel Indigo = ColorBuilder.FromRGBA(75, 0, 130, 255); + public static readonly TPixel Indigo = ColorBuilder.FromRgba(75, 0, 130, 255); /// /// Represents a matching the W3C definition that has an hex value of #FFFFF0. /// - public static readonly TPixel Ivory = ColorBuilder.FromRGBA(255, 255, 240, 255); + public static readonly TPixel Ivory = ColorBuilder.FromRgba(255, 255, 240, 255); /// /// Represents a matching the W3C definition that has an hex value of #F0E68C. /// - public static readonly TPixel Khaki = ColorBuilder.FromRGBA(240, 230, 140, 255); + public static readonly TPixel Khaki = ColorBuilder.FromRgba(240, 230, 140, 255); /// /// Represents a matching the W3C definition that has an hex value of #E6E6FA. /// - public static readonly TPixel Lavender = ColorBuilder.FromRGBA(230, 230, 250, 255); + public static readonly TPixel Lavender = ColorBuilder.FromRgba(230, 230, 250, 255); /// /// Represents a matching the W3C definition that has an hex value of #FFF0F5. /// - public static readonly TPixel LavenderBlush = ColorBuilder.FromRGBA(255, 240, 245, 255); + public static readonly TPixel LavenderBlush = ColorBuilder.FromRgba(255, 240, 245, 255); /// /// Represents a matching the W3C definition that has an hex value of #7CFC00. /// - public static readonly TPixel LawnGreen = ColorBuilder.FromRGBA(124, 252, 0, 255); + public static readonly TPixel LawnGreen = ColorBuilder.FromRgba(124, 252, 0, 255); /// /// Represents a matching the W3C definition that has an hex value of #FFFACD. /// - public static readonly TPixel LemonChiffon = ColorBuilder.FromRGBA(255, 250, 205, 255); + public static readonly TPixel LemonChiffon = ColorBuilder.FromRgba(255, 250, 205, 255); /// /// Represents a matching the W3C definition that has an hex value of #ADD8E6. /// - public static readonly TPixel LightBlue = ColorBuilder.FromRGBA(173, 216, 230, 255); + public static readonly TPixel LightBlue = ColorBuilder.FromRgba(173, 216, 230, 255); /// /// Represents a matching the W3C definition that has an hex value of #F08080. /// - public static readonly TPixel LightCoral = ColorBuilder.FromRGBA(240, 128, 128, 255); + public static readonly TPixel LightCoral = ColorBuilder.FromRgba(240, 128, 128, 255); /// /// Represents a matching the W3C definition that has an hex value of #E0FFFF. /// - public static readonly TPixel LightCyan = ColorBuilder.FromRGBA(224, 255, 255, 255); + public static readonly TPixel LightCyan = ColorBuilder.FromRgba(224, 255, 255, 255); /// /// Represents a matching the W3C definition that has an hex value of #FAFAD2. /// - public static readonly TPixel LightGoldenrodYellow = ColorBuilder.FromRGBA(250, 250, 210, 255); + public static readonly TPixel LightGoldenrodYellow = ColorBuilder.FromRgba(250, 250, 210, 255); /// /// Represents a matching the W3C definition that has an hex value of #D3D3D3. /// - public static readonly TPixel LightGray = ColorBuilder.FromRGBA(211, 211, 211, 255); + public static readonly TPixel LightGray = ColorBuilder.FromRgba(211, 211, 211, 255); /// /// Represents a matching the W3C definition that has an hex value of #90EE90. /// - public static readonly TPixel LightGreen = ColorBuilder.FromRGBA(144, 238, 144, 255); + public static readonly TPixel LightGreen = ColorBuilder.FromRgba(144, 238, 144, 255); /// /// Represents a matching the W3C definition that has an hex value of #FFB6C1. /// - public static readonly TPixel LightPink = ColorBuilder.FromRGBA(255, 182, 193, 255); + public static readonly TPixel LightPink = ColorBuilder.FromRgba(255, 182, 193, 255); /// /// Represents a matching the W3C definition that has an hex value of #FFA07A. /// - public static readonly TPixel LightSalmon = ColorBuilder.FromRGBA(255, 160, 122, 255); + public static readonly TPixel LightSalmon = ColorBuilder.FromRgba(255, 160, 122, 255); /// /// Represents a matching the W3C definition that has an hex value of #20B2AA. /// - public static readonly TPixel LightSeaGreen = ColorBuilder.FromRGBA(32, 178, 170, 255); + public static readonly TPixel LightSeaGreen = ColorBuilder.FromRgba(32, 178, 170, 255); /// /// Represents a matching the W3C definition that has an hex value of #87CEFA. /// - public static readonly TPixel LightSkyBlue = ColorBuilder.FromRGBA(135, 206, 250, 255); + public static readonly TPixel LightSkyBlue = ColorBuilder.FromRgba(135, 206, 250, 255); /// /// Represents a matching the W3C definition that has an hex value of #778899. /// - public static readonly TPixel LightSlateGray = ColorBuilder.FromRGBA(119, 136, 153, 255); + public static readonly TPixel LightSlateGray = ColorBuilder.FromRgba(119, 136, 153, 255); /// /// Represents a matching the W3C definition that has an hex value of #B0C4DE. /// - public static readonly TPixel LightSteelBlue = ColorBuilder.FromRGBA(176, 196, 222, 255); + public static readonly TPixel LightSteelBlue = ColorBuilder.FromRgba(176, 196, 222, 255); /// /// Represents a matching the W3C definition that has an hex value of #FFFFE0. /// - public static readonly TPixel LightYellow = ColorBuilder.FromRGBA(255, 255, 224, 255); + public static readonly TPixel LightYellow = ColorBuilder.FromRgba(255, 255, 224, 255); /// /// Represents a matching the W3C definition that has an hex value of #00FF00. /// - public static readonly TPixel Lime = ColorBuilder.FromRGBA(0, 255, 0, 255); + public static readonly TPixel Lime = ColorBuilder.FromRgba(0, 255, 0, 255); /// /// Represents a matching the W3C definition that has an hex value of #32CD32. /// - public static readonly TPixel LimeGreen = ColorBuilder.FromRGBA(50, 205, 50, 255); + public static readonly TPixel LimeGreen = ColorBuilder.FromRgba(50, 205, 50, 255); /// /// Represents a matching the W3C definition that has an hex value of #FAF0E6. /// - public static readonly TPixel Linen = ColorBuilder.FromRGBA(250, 240, 230, 255); + public static readonly TPixel Linen = ColorBuilder.FromRgba(250, 240, 230, 255); /// /// Represents a matching the W3C definition that has an hex value of #FF00FF. /// - public static readonly TPixel Magenta = ColorBuilder.FromRGBA(255, 0, 255, 255); + public static readonly TPixel Magenta = ColorBuilder.FromRgba(255, 0, 255, 255); /// /// Represents a matching the W3C definition that has an hex value of #800000. /// - public static readonly TPixel Maroon = ColorBuilder.FromRGBA(128, 0, 0, 255); + public static readonly TPixel Maroon = ColorBuilder.FromRgba(128, 0, 0, 255); /// /// Represents a matching the W3C definition that has an hex value of #66CDAA. /// - public static readonly TPixel MediumAquamarine = ColorBuilder.FromRGBA(102, 205, 170, 255); + public static readonly TPixel MediumAquamarine = ColorBuilder.FromRgba(102, 205, 170, 255); /// /// Represents a matching the W3C definition that has an hex value of #0000CD. /// - public static readonly TPixel MediumBlue = ColorBuilder.FromRGBA(0, 0, 205, 255); + public static readonly TPixel MediumBlue = ColorBuilder.FromRgba(0, 0, 205, 255); /// /// Represents a matching the W3C definition that has an hex value of #BA55D3. /// - public static readonly TPixel MediumOrchid = ColorBuilder.FromRGBA(186, 85, 211, 255); + public static readonly TPixel MediumOrchid = ColorBuilder.FromRgba(186, 85, 211, 255); /// /// Represents a matching the W3C definition that has an hex value of #9370DB. /// - public static readonly TPixel MediumPurple = ColorBuilder.FromRGBA(147, 112, 219, 255); + public static readonly TPixel MediumPurple = ColorBuilder.FromRgba(147, 112, 219, 255); /// /// Represents a matching the W3C definition that has an hex value of #3CB371. /// - public static readonly TPixel MediumSeaGreen = ColorBuilder.FromRGBA(60, 179, 113, 255); + public static readonly TPixel MediumSeaGreen = ColorBuilder.FromRgba(60, 179, 113, 255); /// /// Represents a matching the W3C definition that has an hex value of #7B68EE. /// - public static readonly TPixel MediumSlateBlue = ColorBuilder.FromRGBA(123, 104, 238, 255); + public static readonly TPixel MediumSlateBlue = ColorBuilder.FromRgba(123, 104, 238, 255); /// /// Represents a matching the W3C definition that has an hex value of #00FA9A. /// - public static readonly TPixel MediumSpringGreen = ColorBuilder.FromRGBA(0, 250, 154, 255); + public static readonly TPixel MediumSpringGreen = ColorBuilder.FromRgba(0, 250, 154, 255); /// /// Represents a matching the W3C definition that has an hex value of #48D1CC. /// - public static readonly TPixel MediumTurquoise = ColorBuilder.FromRGBA(72, 209, 204, 255); + public static readonly TPixel MediumTurquoise = ColorBuilder.FromRgba(72, 209, 204, 255); /// /// Represents a matching the W3C definition that has an hex value of #C71585. /// - public static readonly TPixel MediumVioletRed = ColorBuilder.FromRGBA(199, 21, 133, 255); + public static readonly TPixel MediumVioletRed = ColorBuilder.FromRgba(199, 21, 133, 255); /// /// Represents a matching the W3C definition that has an hex value of #191970. /// - public static readonly TPixel MidnightBlue = ColorBuilder.FromRGBA(25, 25, 112, 255); + public static readonly TPixel MidnightBlue = ColorBuilder.FromRgba(25, 25, 112, 255); /// /// Represents a matching the W3C definition that has an hex value of #F5FFFA. /// - public static readonly TPixel MintCream = ColorBuilder.FromRGBA(245, 255, 250, 255); + public static readonly TPixel MintCream = ColorBuilder.FromRgba(245, 255, 250, 255); /// /// Represents a matching the W3C definition that has an hex value of #FFE4E1. /// - public static readonly TPixel MistyRose = ColorBuilder.FromRGBA(255, 228, 225, 255); + public static readonly TPixel MistyRose = ColorBuilder.FromRgba(255, 228, 225, 255); /// /// Represents a matching the W3C definition that has an hex value of #FFE4B5. /// - public static readonly TPixel Moccasin = ColorBuilder.FromRGBA(255, 228, 181, 255); + public static readonly TPixel Moccasin = ColorBuilder.FromRgba(255, 228, 181, 255); /// /// Represents a matching the W3C definition that has an hex value of #FFDEAD. /// - public static readonly TPixel NavajoWhite = ColorBuilder.FromRGBA(255, 222, 173, 255); + public static readonly TPixel NavajoWhite = ColorBuilder.FromRgba(255, 222, 173, 255); /// /// Represents a matching the W3C definition that has an hex value of #000080. /// - public static readonly TPixel Navy = ColorBuilder.FromRGBA(0, 0, 128, 255); + public static readonly TPixel Navy = ColorBuilder.FromRgba(0, 0, 128, 255); /// /// Represents a matching the W3C definition that has an hex value of #FDF5E6. /// - public static readonly TPixel OldLace = ColorBuilder.FromRGBA(253, 245, 230, 255); + public static readonly TPixel OldLace = ColorBuilder.FromRgba(253, 245, 230, 255); /// /// Represents a matching the W3C definition that has an hex value of #808000. /// - public static readonly TPixel Olive = ColorBuilder.FromRGBA(128, 128, 0, 255); + public static readonly TPixel Olive = ColorBuilder.FromRgba(128, 128, 0, 255); /// /// Represents a matching the W3C definition that has an hex value of #6B8E23. /// - public static readonly TPixel OliveDrab = ColorBuilder.FromRGBA(107, 142, 35, 255); + public static readonly TPixel OliveDrab = ColorBuilder.FromRgba(107, 142, 35, 255); /// /// Represents a matching the W3C definition that has an hex value of #FFA500. /// - public static readonly TPixel Orange = ColorBuilder.FromRGBA(255, 165, 0, 255); + public static readonly TPixel Orange = ColorBuilder.FromRgba(255, 165, 0, 255); /// /// Represents a matching the W3C definition that has an hex value of #FF4500. /// - public static readonly TPixel OrangeRed = ColorBuilder.FromRGBA(255, 69, 0, 255); + public static readonly TPixel OrangeRed = ColorBuilder.FromRgba(255, 69, 0, 255); /// /// Represents a matching the W3C definition that has an hex value of #DA70D6. /// - public static readonly TPixel Orchid = ColorBuilder.FromRGBA(218, 112, 214, 255); + public static readonly TPixel Orchid = ColorBuilder.FromRgba(218, 112, 214, 255); /// /// Represents a matching the W3C definition that has an hex value of #EEE8AA. /// - public static readonly TPixel PaleGoldenrod = ColorBuilder.FromRGBA(238, 232, 170, 255); + public static readonly TPixel PaleGoldenrod = ColorBuilder.FromRgba(238, 232, 170, 255); /// /// Represents a matching the W3C definition that has an hex value of #98FB98. /// - public static readonly TPixel PaleGreen = ColorBuilder.FromRGBA(152, 251, 152, 255); + public static readonly TPixel PaleGreen = ColorBuilder.FromRgba(152, 251, 152, 255); /// /// Represents a matching the W3C definition that has an hex value of #AFEEEE. /// - public static readonly TPixel PaleTurquoise = ColorBuilder.FromRGBA(175, 238, 238, 255); + public static readonly TPixel PaleTurquoise = ColorBuilder.FromRgba(175, 238, 238, 255); /// /// Represents a matching the W3C definition that has an hex value of #DB7093. /// - public static readonly TPixel PaleVioletRed = ColorBuilder.FromRGBA(219, 112, 147, 255); + public static readonly TPixel PaleVioletRed = ColorBuilder.FromRgba(219, 112, 147, 255); /// /// Represents a matching the W3C definition that has an hex value of #FFEFD5. /// - public static readonly TPixel PapayaWhip = ColorBuilder.FromRGBA(255, 239, 213, 255); + public static readonly TPixel PapayaWhip = ColorBuilder.FromRgba(255, 239, 213, 255); /// /// Represents a matching the W3C definition that has an hex value of #FFDAB9. /// - public static readonly TPixel PeachPuff = ColorBuilder.FromRGBA(255, 218, 185, 255); + public static readonly TPixel PeachPuff = ColorBuilder.FromRgba(255, 218, 185, 255); /// /// Represents a matching the W3C definition that has an hex value of #CD853F. /// - public static readonly TPixel Peru = ColorBuilder.FromRGBA(205, 133, 63, 255); + public static readonly TPixel Peru = ColorBuilder.FromRgba(205, 133, 63, 255); /// /// Represents a matching the W3C definition that has an hex value of #FFC0CB. /// - public static readonly TPixel Pink = ColorBuilder.FromRGBA(255, 192, 203, 255); + public static readonly TPixel Pink = ColorBuilder.FromRgba(255, 192, 203, 255); /// /// Represents a matching the W3C definition that has an hex value of #DDA0DD. /// - public static readonly TPixel Plum = ColorBuilder.FromRGBA(221, 160, 221, 255); + public static readonly TPixel Plum = ColorBuilder.FromRgba(221, 160, 221, 255); /// /// Represents a matching the W3C definition that has an hex value of #B0E0E6. /// - public static readonly TPixel PowderBlue = ColorBuilder.FromRGBA(176, 224, 230, 255); + public static readonly TPixel PowderBlue = ColorBuilder.FromRgba(176, 224, 230, 255); /// /// Represents a matching the W3C definition that has an hex value of #800080. /// - public static readonly TPixel Purple = ColorBuilder.FromRGBA(128, 0, 128, 255); + public static readonly TPixel Purple = ColorBuilder.FromRgba(128, 0, 128, 255); /// /// Represents a matching the W3C definition that has an hex value of #663399. /// - public static readonly TPixel RebeccaPurple = ColorBuilder.FromRGBA(102, 51, 153, 255); + public static readonly TPixel RebeccaPurple = ColorBuilder.FromRgba(102, 51, 153, 255); /// /// Represents a matching the W3C definition that has an hex value of #FF0000. /// - public static readonly TPixel Red = ColorBuilder.FromRGBA(255, 0, 0, 255); + public static readonly TPixel Red = ColorBuilder.FromRgba(255, 0, 0, 255); /// /// Represents a matching the W3C definition that has an hex value of #BC8F8F. /// - public static readonly TPixel RosyBrown = ColorBuilder.FromRGBA(188, 143, 143, 255); + public static readonly TPixel RosyBrown = ColorBuilder.FromRgba(188, 143, 143, 255); /// /// Represents a matching the W3C definition that has an hex value of #4169E1. /// - public static readonly TPixel RoyalBlue = ColorBuilder.FromRGBA(65, 105, 225, 255); + public static readonly TPixel RoyalBlue = ColorBuilder.FromRgba(65, 105, 225, 255); /// /// Represents a matching the W3C definition that has an hex value of #8B4513. /// - public static readonly TPixel SaddleBrown = ColorBuilder.FromRGBA(139, 69, 19, 255); + public static readonly TPixel SaddleBrown = ColorBuilder.FromRgba(139, 69, 19, 255); /// /// Represents a matching the W3C definition that has an hex value of #FA8072. /// - public static readonly TPixel Salmon = ColorBuilder.FromRGBA(250, 128, 114, 255); + public static readonly TPixel Salmon = ColorBuilder.FromRgba(250, 128, 114, 255); /// /// Represents a matching the W3C definition that has an hex value of #F4A460. /// - public static readonly TPixel SandyBrown = ColorBuilder.FromRGBA(244, 164, 96, 255); + public static readonly TPixel SandyBrown = ColorBuilder.FromRgba(244, 164, 96, 255); /// /// Represents a matching the W3C definition that has an hex value of #2E8B57. /// - public static readonly TPixel SeaGreen = ColorBuilder.FromRGBA(46, 139, 87, 255); + public static readonly TPixel SeaGreen = ColorBuilder.FromRgba(46, 139, 87, 255); /// /// Represents a matching the W3C definition that has an hex value of #FFF5EE. /// - public static readonly TPixel SeaShell = ColorBuilder.FromRGBA(255, 245, 238, 255); + public static readonly TPixel SeaShell = ColorBuilder.FromRgba(255, 245, 238, 255); /// /// Represents a matching the W3C definition that has an hex value of #A0522D. /// - public static readonly TPixel Sienna = ColorBuilder.FromRGBA(160, 82, 45, 255); + public static readonly TPixel Sienna = ColorBuilder.FromRgba(160, 82, 45, 255); /// /// Represents a matching the W3C definition that has an hex value of #C0C0C0. /// - public static readonly TPixel Silver = ColorBuilder.FromRGBA(192, 192, 192, 255); + public static readonly TPixel Silver = ColorBuilder.FromRgba(192, 192, 192, 255); /// /// Represents a matching the W3C definition that has an hex value of #87CEEB. /// - public static readonly TPixel SkyBlue = ColorBuilder.FromRGBA(135, 206, 235, 255); + public static readonly TPixel SkyBlue = ColorBuilder.FromRgba(135, 206, 235, 255); /// /// Represents a matching the W3C definition that has an hex value of #6A5ACD. /// - public static readonly TPixel SlateBlue = ColorBuilder.FromRGBA(106, 90, 205, 255); + public static readonly TPixel SlateBlue = ColorBuilder.FromRgba(106, 90, 205, 255); /// /// Represents a matching the W3C definition that has an hex value of #708090. /// - public static readonly TPixel SlateGray = ColorBuilder.FromRGBA(112, 128, 144, 255); + public static readonly TPixel SlateGray = ColorBuilder.FromRgba(112, 128, 144, 255); /// /// Represents a matching the W3C definition that has an hex value of #FFFAFA. /// - public static readonly TPixel Snow = ColorBuilder.FromRGBA(255, 250, 250, 255); + public static readonly TPixel Snow = ColorBuilder.FromRgba(255, 250, 250, 255); /// /// Represents a matching the W3C definition that has an hex value of #00FF7F. /// - public static readonly TPixel SpringGreen = ColorBuilder.FromRGBA(0, 255, 127, 255); + public static readonly TPixel SpringGreen = ColorBuilder.FromRgba(0, 255, 127, 255); /// /// Represents a matching the W3C definition that has an hex value of #4682B4. /// - public static readonly TPixel SteelBlue = ColorBuilder.FromRGBA(70, 130, 180, 255); + public static readonly TPixel SteelBlue = ColorBuilder.FromRgba(70, 130, 180, 255); /// /// Represents a matching the W3C definition that has an hex value of #D2B48C. /// - public static readonly TPixel Tan = ColorBuilder.FromRGBA(210, 180, 140, 255); + public static readonly TPixel Tan = ColorBuilder.FromRgba(210, 180, 140, 255); /// /// Represents a matching the W3C definition that has an hex value of #008080. /// - public static readonly TPixel Teal = ColorBuilder.FromRGBA(0, 128, 128, 255); + public static readonly TPixel Teal = ColorBuilder.FromRgba(0, 128, 128, 255); /// /// Represents a matching the W3C definition that has an hex value of #D8BFD8. /// - public static readonly TPixel Thistle = ColorBuilder.FromRGBA(216, 191, 216, 255); + public static readonly TPixel Thistle = ColorBuilder.FromRgba(216, 191, 216, 255); /// /// Represents a matching the W3C definition that has an hex value of #FF6347. /// - public static readonly TPixel Tomato = ColorBuilder.FromRGBA(255, 99, 71, 255); + public static readonly TPixel Tomato = ColorBuilder.FromRgba(255, 99, 71, 255); /// /// Represents a matching the W3C definition that has an hex value of #FFFFFF. /// - public static readonly TPixel Transparent = ColorBuilder.FromRGBA(255, 255, 255, 0); + public static readonly TPixel Transparent = ColorBuilder.FromRgba(255, 255, 255, 0); /// /// Represents a matching the W3C definition that has an hex value of #40E0D0. /// - public static readonly TPixel Turquoise = ColorBuilder.FromRGBA(64, 224, 208, 255); + public static readonly TPixel Turquoise = ColorBuilder.FromRgba(64, 224, 208, 255); /// /// Represents a matching the W3C definition that has an hex value of #EE82EE. /// - public static readonly TPixel Violet = ColorBuilder.FromRGBA(238, 130, 238, 255); + public static readonly TPixel Violet = ColorBuilder.FromRgba(238, 130, 238, 255); /// /// Represents a matching the W3C definition that has an hex value of #F5DEB3. /// - public static readonly TPixel Wheat = ColorBuilder.FromRGBA(245, 222, 179, 255); + public static readonly TPixel Wheat = ColorBuilder.FromRgba(245, 222, 179, 255); /// /// Represents a matching the W3C definition that has an hex value of #FFFFFF. /// - public static readonly TPixel White = ColorBuilder.FromRGBA(255, 255, 255, 255); + public static readonly TPixel White = ColorBuilder.FromRgba(255, 255, 255, 255); /// /// Represents a matching the W3C definition that has an hex value of #F5F5F5. /// - public static readonly TPixel WhiteSmoke = ColorBuilder.FromRGBA(245, 245, 245, 255); + public static readonly TPixel WhiteSmoke = ColorBuilder.FromRgba(245, 245, 245, 255); /// /// Represents a matching the W3C definition that has an hex value of #FFFF00. /// - public static readonly TPixel Yellow = ColorBuilder.FromRGBA(255, 255, 0, 255); + public static readonly TPixel Yellow = ColorBuilder.FromRgba(255, 255, 0, 255); /// /// Represents a matching the W3C definition that has an hex value of #9ACD32. /// - public static readonly TPixel YellowGreen = ColorBuilder.FromRGBA(154, 205, 50, 255); + public static readonly TPixel YellowGreen = ColorBuilder.FromRgba(154, 205, 50, 255); /// /// Gets a collection of web safe, colors as defined in the CSS Color Module Level 4. From e492b684029e863dc54a68ee037234833384d1d8 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 11 May 2019 15:49:58 +0200 Subject: [PATCH 166/223] temporarily disable target frameworks --- src/ImageSharp.Drawing/ImageSharp.Drawing.csproj | 3 ++- src/ImageSharp/ImageSharp.csproj | 3 ++- tests/ImageSharp.Tests/ImageSharp.Tests.csproj | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj index 915915f6cc..fdeb27a770 100644 --- a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj +++ b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj @@ -11,7 +11,8 @@ $(packageversion) 0.0.1 - netcoreapp2.1;netstandard1.3;netstandard2.0 + + netstandard2.0 7.3 true diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index d734648f41..4abe2f57a2 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -11,7 +11,8 @@ $(packageversion) 0.0.1 - netcoreapp2.1;netstandard1.3;netstandard2.0;net472 + + netstandard2.0 true true diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index 0b727f30ce..efdf89964c 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -1,6 +1,7 @@  - netcoreapp2.1;net462;net472 + + netcoreapp2.1 True latest full From 09d6fe7ccf59c6e2de8f15e0e8f15372f61b0552 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sat, 11 May 2019 16:27:51 +0200 Subject: [PATCH 167/223] Enabled dither again --- src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index be82e2cab3..8d0e8578d1 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -304,7 +304,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp byte[] colorPalette = new byte[ColorPaletteSize8Bit]; #endif - var quantizer = new OctreeQuantizer(dither: false, maxColors: 256); + var quantizer = new OctreeQuantizer(dither: true, maxColors: 256); QuantizedFrame quantized = quantizer.CreateFrameQuantizer(this.configuration).QuantizeFrame(image); int idx = 0; From 5c571f5d9f16c2f6aa78b4f07af1289ffb9c7f1f Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 11 May 2019 17:00:16 +0200 Subject: [PATCH 168/223] validating tests for: DrawPath, FillComplexPolygon --- .../ImageSharp.Tests/Drawing/DrawPathTests.cs | 133 +++++++----------- .../Drawing/FillComplexPolygonTests.cs | 62 ++++++++ ...tternTests.cs => FillPatternBrushTests.cs} | 6 +- .../Drawing/SolidComplexPolygonTests.cs | 109 -------------- .../Processors/Overlays/OverlayTestBase.cs | 3 +- .../TestUtilities/TestUtils.cs | 9 +- tests/Images/External | 2 +- 7 files changed, 125 insertions(+), 199 deletions(-) create mode 100644 tests/ImageSharp.Tests/Drawing/FillComplexPolygonTests.cs rename tests/ImageSharp.Tests/Drawing/{FillPatternTests.cs => FillPatternBrushTests.cs} (98%) delete mode 100644 tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs diff --git a/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs b/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs index d2485f334a..c638107b81 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs @@ -1,112 +1,79 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; using System.Numerics; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; +using SixLabors.Primitives; using SixLabors.Shapes; using Xunit; namespace SixLabors.ImageSharp.Tests.Drawing { - public class DrawPathTests : FileTestBase + [GroupOutput("Drawing")] + public class DrawPathTests { - [Fact] - public void ImageShouldBeOverlayedByPath() + public static readonly TheoryData DrawPathData = new TheoryData + { + { "White", 255, 1.5f }, + { "Red", 255, 3 }, + { "HotPink", 255, 5 }, + { "HotPink", 150, 5 }, + { "White", 255, 15 }, + }; + + [Theory] + [WithSolidFilledImages(nameof(DrawPathData), 300, 450, "Blue", PixelTypes.Rgba32)] + public void DrawPath(TestImageProvider provider, string colorName, byte alpha, float thickness) + where TPixel : struct, IPixel { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "Path"); - using (var image = new Image(500, 500)) - { - var linerSegemnt = new LinearLineSegment( - new Vector2(10, 10), - new Vector2(200, 150), - new Vector2(50, 300)); - var bazierSegment = new CubicBezierLineSegment( - new Vector2(50, 300), - new Vector2(500, 500), - new Vector2(60, 10), - new Vector2(10, 400)); - - var p = new Path(linerSegemnt, bazierSegment); - - image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); - image.Mutate(x => x.Draw(Rgba32.HotPink, 5, p)); - image.Save($"{path}/Simple.png"); - - Buffer2D sourcePixels = image.GetRootFramePixelBuffer(); - Assert.Equal(Rgba32.HotPink, sourcePixels[11, 11]); - - Assert.Equal(Rgba32.HotPink, sourcePixels[199, 149]); - - Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]); - } - } - - - [Fact] - public void ImageShouldBeOverlayedPathWithOpacity() - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "Path"); - - var color = new Rgba32(Rgba32.HotPink.R, Rgba32.HotPink.G, Rgba32.HotPink.B, 150); - - var linerSegemnt = new LinearLineSegment( - new Vector2(10, 10), - new Vector2(200, 150), - new Vector2(50, 300) - ); - - var bazierSegment = new CubicBezierLineSegment(new Vector2(50, 300), + new Vector2(10, 10), + new Vector2(200, 150), + new Vector2(50, 300)); + var bazierSegment = new CubicBezierLineSegment( + new Vector2(50, 300), new Vector2(500, 500), new Vector2(60, 10), new Vector2(10, 400)); - var p = new Path(linerSegemnt, bazierSegment); - - using (var image = new Image(500, 500)) - { - image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); - image.Mutate(x => x.Draw(color, 10, p)); - image.Save($"{path}/Opacity.png"); + var path = new Path(linerSegemnt, bazierSegment); - //shift background color towards forground color by the opacity amount - var mergedColor = new Rgba32( - Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f)); + Rgba32 rgba = TestUtils.GetColorByName(colorName); + rgba.A = alpha; + Color color = rgba; - Buffer2D sourcePixels = image.GetRootFramePixelBuffer(); - Assert.Equal(mergedColor, sourcePixels[11, 11]); + FormattableString testDetails = $"{colorName}_A{alpha}_T{thickness}"; - Assert.Equal(mergedColor, sourcePixels[199, 149]); - - Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]); - } + provider.RunValidatingProcessorTest( + x => x.Draw(color.ToPixel(), thickness, path), + testDetails, + appendPixelTypeToFileName: false, + appendSourceFileOrDescription: false); } - [Fact] - public void PathExtendingOffEdgeOfImageShouldNotBeCropped() + [Theory] + [WithSolidFilledImages(256, 256, "Black", PixelTypes.Rgba32)] + public void PathExtendingOffEdgeOfImageShouldNotBeCropped(TestImageProvider provider) + where TPixel : struct, IPixel { - - string path = TestEnvironment.CreateOutputDirectory("Drawing", "Path"); - using (var image = new Image(256, 256)) - { - image.Mutate(x => x.Fill(Rgba32.Black)); - Pen pen = Pens.Solid(Rgba32.White, 5f); - - for (int i = 0; i < 300; i += 20) - { - image.Mutate( - x => x.DrawLines( - pen, - new SixLabors.Primitives.PointF[] { new Vector2(100, 2), new Vector2(-10, i) })); - } - - image.Save($"{path}/ClippedLines.png"); - Buffer2D sourcePixels = image.GetRootFramePixelBuffer(); - Assert.Equal(Rgba32.White, sourcePixels[0, 90]); - } + Color color = Color.White; + Pen pen = Pens.Solid(color.ToPixel(), 5f); + + provider.RunValidatingProcessorTest( + x => + { + for (int i = 0; i < 300; i += 20) + { + PointF[] points = new PointF[] { new Vector2(100, 2), new Vector2(-10, i) }; + x.DrawLines(pen, points); + } + }, + appendPixelTypeToFileName: false, + appendSourceFileOrDescription: false); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Drawing/FillComplexPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/FillComplexPolygonTests.cs new file mode 100644 index 0000000000..3887cb9a7a --- /dev/null +++ b/tests/ImageSharp.Tests/Drawing/FillComplexPolygonTests.cs @@ -0,0 +1,62 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; + +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.Shapes; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Drawing +{ + [GroupOutput("Drawing")] + public class FillComplexPolygonTests + { + [Theory] + [WithSolidFilledImages(300, 400, "Blue", PixelTypes.Rgba32, false, false)] + [WithSolidFilledImages(300, 400, "Blue", PixelTypes.Rgba32, true, false)] + [WithSolidFilledImages(300, 400, "Blue", PixelTypes.Rgba32, false, true)] + public void ComplexPolygon_SolidFill(TestImageProvider provider, bool overlap, bool transparent) + where TPixel :struct, IPixel + { + var simplePath = new Polygon(new LinearLineSegment( + new Vector2(10, 10), + new Vector2(200, 150), + new Vector2(50, 300))); + + var hole1 = new Polygon(new LinearLineSegment( + new Vector2(37, 85), + overlap ? new Vector2(130, 40) : new Vector2(93, 85), + new Vector2(65, 137))); + IPath clipped = simplePath.Clip(hole1); + + Rgba32 colorRgba = Rgba32.HotPink; + if (transparent) + { + colorRgba.A = 150; + } + + Color color = colorRgba; + + string testDetails = ""; + if (overlap) + { + testDetails += "_Overlap"; + } + + if (transparent) + { + testDetails += "_Transparent"; + } + + provider.RunValidatingProcessorTest( + x => x.Fill(color.ToPixel(), clipped), + testDetails, + appendPixelTypeToFileName: false, + appendSourceFileOrDescription: false); + } + } +} diff --git a/tests/ImageSharp.Tests/Drawing/FillPatternTests.cs b/tests/ImageSharp.Tests/Drawing/FillPatternBrushTests.cs similarity index 98% rename from tests/ImageSharp.Tests/Drawing/FillPatternTests.cs rename to tests/ImageSharp.Tests/Drawing/FillPatternBrushTests.cs index b310c7afc6..dcc689fe1c 100644 --- a/tests/ImageSharp.Tests/Drawing/FillPatternTests.cs +++ b/tests/ImageSharp.Tests/Drawing/FillPatternBrushTests.cs @@ -12,11 +12,11 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Drawing { - public class FillPatternBrushTests : FileTestBase + public class FillPatternBrushTests { private void Test(string name, Rgba32 background, IBrush brush, Rgba32[,] expectedPattern) { - string path = TestEnvironment.CreateOutputDirectory("Fill", "PatternBrush"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "FillPatternBrushTests"); using (var image = new Image(20, 20)) { image.Mutate(x => x.Fill(background).Fill(brush)); @@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing } } - image.Mutate(x => x.Resize(80, 80)); + image.Mutate(x => x.Resize(80, 80, KnownResamplers.NearestNeighbor)); image.Save($"{path}/{name}x4.png"); } } diff --git a/tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs deleted file mode 100644 index 0e997ad7eb..0000000000 --- a/tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Numerics; - -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.Shapes; - -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Drawing -{ - public class SolidComplexPolygonTests : FileTestBase - { - [Fact] - public void ImageShouldBeOverlayedByPolygonOutline() - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "ComplexPolygon"); - var simplePath = new Polygon(new LinearLineSegment( - new Vector2(10, 10), - new Vector2(200, 150), - new Vector2(50, 300))); - - var hole1 = new Polygon(new LinearLineSegment( - new Vector2(37, 85), - new Vector2(93, 85), - new Vector2(65, 137))); - IPath clipped = simplePath.Clip(hole1); - // var clipped = new Rectangle(10, 10, 100, 100).Clip(new Rectangle(20, 0, 20, 20)); - using (var image = new Image(500, 500)) - { - image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); - image.Mutate(x => x.Fill(Rgba32.HotPink, clipped)); - image.Save($"{path}/Simple.png"); - - Buffer2D sourcePixels = image.GetRootFramePixelBuffer(); - Assert.Equal(Rgba32.HotPink, sourcePixels[20, 35]); - - //inside hole - Assert.Equal(Rgba32.Blue, sourcePixels[60, 100]); - } - } - - - [Fact] - public void ImageShouldBeOverlayedPolygonOutlineWithOverlap() - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "ComplexPolygon"); - var simplePath = new Polygon(new LinearLineSegment( - new Vector2(10, 10), - new Vector2(200, 150), - new Vector2(50, 300))); - - var hole1 = new Polygon(new LinearLineSegment( - new Vector2(37, 85), - new Vector2(130, 40), - new Vector2(65, 137))); - - using (var image = new Image(500, 500)) - { - image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); - image.Mutate(x => x.Fill(Rgba32.HotPink, simplePath.Clip(hole1))); - image.Save($"{path}/SimpleOverlapping.png"); - - Buffer2D sourcePixels = image.GetRootFramePixelBuffer(); - Assert.Equal(Rgba32.HotPink, sourcePixels[20, 35]); - - //inside hole - Assert.Equal(Rgba32.Blue, sourcePixels[60, 100]); - } - } - - [Fact] - public void ImageShouldBeOverlayedPolygonOutlineWithOpacity() - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "ComplexPolygon"); - var simplePath = new Polygon(new LinearLineSegment( - new Vector2(10, 10), - new Vector2(200, 150), - new Vector2(50, 300))); - - var hole1 = new Polygon(new LinearLineSegment( - new Vector2(37, 85), - new Vector2(93, 85), - new Vector2(65, 137))); - - var color = new Rgba32(Rgba32.HotPink.R, Rgba32.HotPink.G, Rgba32.HotPink.B, 150); - - using (var image = new Image(500, 500)) - { - image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); - image.Mutate(x => x.Fill(color, simplePath.Clip(hole1))); - image.Save($"{path}/Opacity.png"); - - //shift background color towards forground color by the opacity amount - var mergedColor = new Rgba32( - Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f)); - - Buffer2D sourcePixels = image.GetRootFramePixelBuffer(); - Assert.Equal(mergedColor, sourcePixels[20, 35]); - - //inside hole - Assert.Equal(Rgba32.Blue, sourcePixels[60, 100]); - } - } - } -} diff --git a/tests/ImageSharp.Tests/Processing/Processors/Overlays/OverlayTestBase.cs b/tests/ImageSharp.Tests/Processing/Processors/Overlays/OverlayTestBase.cs index 788f7e1ade..eaad82f9a3 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Overlays/OverlayTestBase.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Overlays/OverlayTestBase.cs @@ -27,8 +27,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Overlays where TPixel : struct, IPixel { provider.Utility.TestGroupName = this.GetType().Name; - var f = (FieldInfo)typeof(Color).GetMember(colorName)[0]; - Color color = (Color)f.GetValue(null); + Color color = TestUtils.GetColorByName(colorName); provider.RunValidatingProcessorTest(x => this.Apply(x, color), colorName, ValidatorComparer, appendPixelTypeToFileName: false); } diff --git a/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs b/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs index e51aa28d8f..5db0c08282 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs @@ -141,8 +141,15 @@ namespace SixLabors.ImageSharp.Tests /// The pixel types internal static PixelTypes[] GetAllPixelTypes() => (PixelTypes[])Enum.GetValues(typeof(PixelTypes)); + internal static Color GetColorByName(string colorName) + { + var f = (FieldInfo)typeof(Color).GetMember(colorName)[0]; + return (Color)f.GetValue(null); + } + internal static TPixel GetPixelOfNamedColor(string colorName) - where TPixel : struct, IPixel => (TPixel)typeof(NamedColors).GetTypeInfo().GetField(colorName).GetValue(null); + where TPixel : struct, IPixel => + GetColorByName(colorName).ToPixel(); /// /// Utility for testing image processor extension methods: diff --git a/tests/Images/External b/tests/Images/External index c057090b44..d83843deed 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit c057090b4402120a83a8efe251aa5b691db9c0dc +Subproject commit d83843deedc43712f29f631aab090640b4f54946 From 445d0f8bf0ca007c49c567adc72768cbfd86da6f Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 11 May 2019 23:53:27 +0200 Subject: [PATCH 169/223] validation in DrawImageTest --- .../ImageSharp.Tests/Drawing/DrawImageTest.cs | 198 ------------------ .../Drawing/DrawImageTests.cs | 174 +++++++++++++++ .../TestUtilities/TestImageExtensions.cs | 25 ++- tests/Images/External | 2 +- 4 files changed, 192 insertions(+), 207 deletions(-) delete mode 100644 tests/ImageSharp.Tests/Drawing/DrawImageTest.cs create mode 100644 tests/ImageSharp.Tests/Drawing/DrawImageTests.cs diff --git a/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs b/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs deleted file mode 100644 index 54b04390ec..0000000000 --- a/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs +++ /dev/null @@ -1,198 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; -using SixLabors.Primitives; -using Xunit; - -namespace SixLabors.ImageSharp.Tests -{ - [GroupOutput("Drawing")] - public class DrawImageTest : FileTestBase - { - private const PixelTypes PixelTypes = Tests.PixelTypes.Rgba32; - - public static readonly string[] TestFiles = { - TestImages.Jpeg.Baseline.Calliphora, - TestImages.Bmp.Car, - TestImages.Png.Splash, - TestImages.Gif.Rings - }; - - [Theory] - [WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Normal)] - [WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Multiply)] - [WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Add)] - [WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Subtract)] - [WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Screen)] - [WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Darken)] - [WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Lighten)] - [WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Overlay)] - [WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.HardLight)] - public void ImageShouldApplyDrawImage(TestImageProvider provider, PixelColorBlendingMode mode) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - using (var blend = Image.Load(TestFile.Create(TestImages.Bmp.Car).Bytes)) - { - blend.Mutate(x => x.Resize(image.Width / 2, image.Height / 2)); - image.Mutate(x => x.DrawImage(blend, new Point(image.Width / 4, image.Height / 4), mode, .75f)); - image.DebugSave(provider, new { mode }); - } - } - - [Theory] - [WithFile(TestImages.Png.Rainbow, PixelTypes, PixelColorBlendingMode.Normal)] - [WithFile(TestImages.Png.Rainbow, PixelTypes, PixelColorBlendingMode.Multiply)] - [WithFile(TestImages.Png.Rainbow, PixelTypes, PixelColorBlendingMode.Add)] - [WithFile(TestImages.Png.Rainbow, PixelTypes, PixelColorBlendingMode.Subtract)] - [WithFile(TestImages.Png.Rainbow, PixelTypes, PixelColorBlendingMode.Screen)] - [WithFile(TestImages.Png.Rainbow, PixelTypes, PixelColorBlendingMode.Darken)] - [WithFile(TestImages.Png.Rainbow, PixelTypes, PixelColorBlendingMode.Lighten)] - [WithFile(TestImages.Png.Rainbow, PixelTypes, PixelColorBlendingMode.Overlay)] - [WithFile(TestImages.Png.Rainbow, PixelTypes, PixelColorBlendingMode.HardLight)] - public void ImageBlendingMatchesSvgSpecExamples(TestImageProvider provider, PixelColorBlendingMode mode) - where TPixel : struct, IPixel - { - using (Image background = provider.GetImage()) - using (var source = Image.Load(TestFile.Create(TestImages.Png.Ducky).Bytes)) - { - background.Mutate(x => x.DrawImage(source, mode, 1F)); - VerifyImage(provider, mode, background); - } - } - - [Theory] - [WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Normal)] - public void ImageShouldDrawTransformedImage(TestImageProvider provider, PixelColorBlendingMode mode) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - using (var blend = Image.Load(TestFile.Create(TestImages.Bmp.Car).Bytes)) - { - AffineTransformBuilder builder = new AffineTransformBuilder() - .AppendRotationDegrees(45F) - .AppendScale(new SizeF(.25F, .25F)) - .AppendTranslation(new PointF(10, 10)); - - // Apply a background color so we can see the translation. - blend.Mutate(x => x.Transform(builder)); - blend.Mutate(x => x.BackgroundColor(Color.HotPink)); - - // Lets center the matrix so we can tell whether any cut-off issues we may have belong to the drawing processor - var position = new Point((image.Width - blend.Width) / 2, (image.Height - blend.Height) / 2); - image.Mutate(x => x.DrawImage(blend, position, mode, .75F)); - image.DebugSave(provider, new[] { "Transformed" }); - } - } - - [Theory] - [WithSolidFilledImages(100, 100, 255, 255, 255, PixelTypes.Rgba32)] - public void ImageShouldHandleNegativeLocation(TestImageProvider provider) - { - using (Image background = provider.GetImage()) - using (var overlay = new Image(50, 50)) - { - overlay.Mutate(x => x.Fill(Rgba32.Black)); - - const int xy = -25; - Rgba32 backgroundPixel = background[0, 0]; - Rgba32 overlayPixel = overlay[Math.Abs(xy) + 1, Math.Abs(xy) + 1]; - - background.Mutate(x => x.DrawImage(overlay, new Point(xy, xy), PixelColorBlendingMode.Normal, 1F)); - - Assert.Equal(Rgba32.White, backgroundPixel); - Assert.Equal(overlayPixel, background[0, 0]); - - background.DebugSave(provider, testOutputDetails: "Negative"); - } - } - - [Theory] - [WithSolidFilledImages(100, 100, 255, 255, 255, PixelTypes.Rgba32)] - public void ImageShouldHandlePositiveLocation(TestImageProvider provider) - { - using (Image background = provider.GetImage()) - using (var overlay = new Image(50, 50)) - { - overlay.Mutate(x => x.Fill(Rgba32.Black)); - - const int xy = 25; - Rgba32 backgroundPixel = background[xy - 1, xy - 1]; - Rgba32 overlayPixel = overlay[0, 0]; - - background.Mutate(x => x.DrawImage(overlay, new Point(xy, xy), PixelColorBlendingMode.Normal, 1F)); - - Assert.Equal(Rgba32.White, backgroundPixel); - Assert.Equal(overlayPixel, background[xy, xy]); - - background.DebugSave(provider, testOutputDetails: "Positive"); - } - } - [Theory] - [WithSolidFilledImages(100, 100, 255, 255, 255, PixelTypes.Rgba32)] - public void ImageShouldHandlePositiveLocationTruncatedOverlay(TestImageProvider provider) - { - using (Image background = provider.GetImage()) - using (var overlay = new Image(50, 50)) - { - overlay.Mutate(x => x.Fill(Rgba32.Black)); - - const int xy = 75; - Rgba32 backgroundPixel = background[xy - 1, xy - 1]; - Rgba32 overlayPixel = overlay[0, 0]; - - background.Mutate(x => x.DrawImage(overlay, new Point(xy, xy), PixelColorBlendingMode.Normal, 1F)); - - Assert.Equal(Rgba32.White, backgroundPixel); - Assert.Equal(overlayPixel, background[xy, xy]); - - background.DebugSave(provider, testOutputDetails: "PositiveTruncated"); - } - } - - [Theory] - [WithSolidFilledImages(100, 100, 255, 255, 255, PixelTypes.Rgba32, -30, -30)] - [WithSolidFilledImages(100, 100, 255, 255, 255, PixelTypes.Rgba32, 130, -30)] - [WithSolidFilledImages(100, 100, 255, 255, 255, PixelTypes.Rgba32, 130, 130)] - [WithSolidFilledImages(100, 100, 255, 255, 255, PixelTypes.Rgba32, -30, 130)] - public void NonOverlappingImageThrows(TestImageProvider provider, int x, int y) - { - using (Image background = provider.GetImage()) - using (var overlay = new Image(Configuration.Default, 10, 10, Rgba32.Black)) - { - ImageProcessingException ex = Assert.Throws(Test); - - Assert.Contains("does not overlap", ex.ToString()); - - void Test() - { - background.Mutate(context => context.DrawImage(overlay, new Point(x, y), GraphicsOptions.Default)); - } - } - } - - private static void VerifyImage( - TestImageProvider provider, - PixelColorBlendingMode mode, - Image img) - where TPixel : struct, IPixel - { - img.DebugSave( - provider, - new { mode }, - appendPixelTypeToFileName: false, - appendSourceFileOrDescription: false); - - var comparer = ImageComparer.TolerantPercentage(0.01F, 3); - img.CompareFirstFrameToReferenceOutput(comparer, - provider, - new { mode }, - appendPixelTypeToFileName: false, - appendSourceFileOrDescription: false); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs b/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs new file mode 100644 index 0000000000..f4e9b7b450 --- /dev/null +++ b/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs @@ -0,0 +1,174 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; +using SixLabors.Primitives; +using SixLabors.Shapes; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Drawing +{ + [GroupOutput("Drawing")] + public class DrawImageTests + { + public static readonly TheoryData BlendingModes = new TheoryData + { + PixelColorBlendingMode.Normal, + PixelColorBlendingMode.Multiply, + PixelColorBlendingMode.Add, + PixelColorBlendingMode.Subtract, + PixelColorBlendingMode.Screen, + PixelColorBlendingMode.Darken, + PixelColorBlendingMode.Lighten, + PixelColorBlendingMode.Overlay, + PixelColorBlendingMode.HardLight, + }; + + [Theory] + [WithFile( TestImages.Png.Rainbow,nameof(BlendingModes), PixelTypes.Rgba32)] + public void ImageBlendingMatchesSvgSpecExamples(TestImageProvider provider, PixelColorBlendingMode mode) + where TPixel : struct, IPixel + { + using (Image background = provider.GetImage()) + using (var source = Image.Load(TestFile.Create(TestImages.Png.Ducky).Bytes)) + { + background.Mutate(x => x.DrawImage(source, mode, 1F)); + background.DebugSave( + provider, + new { mode = mode }, + appendPixelTypeToFileName: false, + appendSourceFileOrDescription: false); + + var comparer = ImageComparer.TolerantPercentage(0.01F); + background.CompareToReferenceOutput(comparer, + provider, + new { mode = mode }, + appendPixelTypeToFileName: false, + appendSourceFileOrDescription: false); + } + } + + [Theory] + [WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Rgba32, TestImages.Png.Splash, PixelColorBlendingMode.Normal, 1f)] + [WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Bgr24, TestImages.Png.Bike, PixelColorBlendingMode.Normal, 1f)] + [WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Rgba32, TestImages.Png.Splash, PixelColorBlendingMode.Normal, 0.75f)] + [WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Rgba32, TestImages.Png.Splash, PixelColorBlendingMode.Normal, 0.25f)] + + [WithTestPatternImages(400, 400, PixelTypes.Rgba32, TestImages.Png.Splash, PixelColorBlendingMode.Multiply, 0.5f)] + [WithTestPatternImages(400, 400, PixelTypes.Rgba32, TestImages.Png.Splash, PixelColorBlendingMode.Add, 0.5f)] + [WithTestPatternImages(400, 400, PixelTypes.Rgba32, TestImages.Png.Splash, PixelColorBlendingMode.Subtract, 0.5f)] + + [WithFile(TestImages.Png.Rgb48Bpp, PixelTypes.Rgba64, TestImages.Png.Splash, PixelColorBlendingMode.Normal, 1f)] + [WithFile(TestImages.Png.Rgb48Bpp, PixelTypes.Rgba64, TestImages.Png.Splash, PixelColorBlendingMode.Normal, 0.25f)] + public void WorksWithDifferentConfigurations( + TestImageProvider provider, + string brushImage, + PixelColorBlendingMode mode, + float opacity) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + using (var blend = Image.Load(TestFile.Create(brushImage).Bytes)) + { + Size size = new Size(image.Width * 3 / 4, image.Height *3/ 4); + Point position = new Point(image.Width / 8, image.Height / 8); + blend.Mutate(x => x.Resize(size.Width, size.Height, KnownResamplers.Bicubic)); + image.Mutate(x => x.DrawImage(blend, position, mode, opacity)); + FormattableString testInfo = $"{System.IO.Path.GetFileNameWithoutExtension(brushImage)}-{mode}-{opacity}"; + + PngEncoder encoder = new PngEncoder(); + + if (provider.PixelType == PixelTypes.Rgba64) + { + encoder.BitDepth = PngBitDepth.Bit16; + } + + image.DebugSave(provider, testInfo, encoder: encoder); + image.CompareToReferenceOutput(provider, testInfo); + } + } + + + [Theory] + [WithSolidFilledImages(100, 100, "White", PixelTypes.Rgba32, 0, 0)] + [WithSolidFilledImages(100, 100, "White", PixelTypes.Rgba32, 25, 25)] + [WithSolidFilledImages(100, 100, "White", PixelTypes.Rgba32, 75, 50)] + [WithSolidFilledImages(100, 100, "White", PixelTypes.Rgba32, -25, -30)] + public void WorksWithDifferentLocations(TestImageProvider provider, int x, int y) + { + using (Image background = provider.GetImage()) + using (var overlay = new Image(50, 50)) + { + overlay.Mutate(c => c.Fill(Rgba32.Black)); + + background.Mutate(c => c.DrawImage(overlay, new Point(x, y), PixelColorBlendingMode.Normal, 1F)); + + background.DebugSave( + provider, + testOutputDetails: $"{x}_{y}", + appendPixelTypeToFileName: false, + appendSourceFileOrDescription: false); + + background.CompareToReferenceOutput( + provider, + testOutputDetails: $"{x}_{y}", + appendPixelTypeToFileName: false, + appendSourceFileOrDescription: false); + } + } + + [Theory] + [WithFile(TestImages.Png.Splash, PixelTypes.Rgba32)] + public void DrawTransformed(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + using (var blend = Image.Load(TestFile.Create(TestImages.Bmp.Car).Bytes)) + { + AffineTransformBuilder builder = new AffineTransformBuilder() + .AppendRotationDegrees(45F) + .AppendScale(new SizeF(.25F, .25F)) + .AppendTranslation(new PointF(10, 10)); + + // Apply a background color so we can see the translation. + blend.Mutate(x => x.Transform(builder)); + blend.Mutate(x => x.BackgroundColor(Color.HotPink)); + + // Lets center the matrix so we can tell whether any cut-off issues we may have belong to the drawing processor + var position = new Point((image.Width - blend.Width) / 2, (image.Height - blend.Height) / 2); + image.Mutate(x => x.DrawImage(blend, position, .75F)); + + image.DebugSave(provider, appendSourceFileOrDescription: false, appendPixelTypeToFileName: false); + image.CompareToReferenceOutput(provider, appendSourceFileOrDescription: false, appendPixelTypeToFileName: false); + } + } + + [Theory] + [WithSolidFilledImages(100, 100, 255, 255, 255, PixelTypes.Rgba32, -30, -30)] + [WithSolidFilledImages(100, 100, 255, 255, 255, PixelTypes.Rgba32, 130, -30)] + [WithSolidFilledImages(100, 100, 255, 255, 255, PixelTypes.Rgba32, 130, 130)] + [WithSolidFilledImages(100, 100, 255, 255, 255, PixelTypes.Rgba32, -30, 130)] + public void NonOverlappingImageThrows(TestImageProvider provider, int x, int y) + { + using (Image background = provider.GetImage()) + using (var overlay = new Image(Configuration.Default, 10, 10, Rgba32.Black)) + { + ImageProcessingException ex = Assert.Throws(Test); + + Assert.Contains("does not overlap", ex.ToString()); + + void Test() + { + background.Mutate(context => context.DrawImage(overlay, new Point(x, y), GraphicsOptions.Default)); + } + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index 6ca3ed6f9f..c5aa1fb944 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -61,7 +61,8 @@ namespace SixLabors.ImageSharp.Tests FormattableString testOutputDetails, string extension = "png", bool appendPixelTypeToFileName = true, - bool appendSourceFileOrDescription = true) + bool appendSourceFileOrDescription = true, + IImageEncoder encoder = null) where TPixel : struct, IPixel { return image.DebugSave( @@ -69,7 +70,8 @@ namespace SixLabors.ImageSharp.Tests (object)testOutputDetails, extension, appendPixelTypeToFileName, - appendSourceFileOrDescription); + appendSourceFileOrDescription, + encoder); } /// @@ -82,13 +84,15 @@ namespace SixLabors.ImageSharp.Tests /// The extension /// A boolean indicating whether to append the pixel type to the output file name. /// A boolean indicating whether to append to the test output file name. + /// Custom encoder to use. public static Image DebugSave( this Image image, ITestImageProvider provider, object testOutputDetails = null, string extension = "png", bool appendPixelTypeToFileName = true, - bool appendSourceFileOrDescription = true) + bool appendSourceFileOrDescription = true, + IImageEncoder encoder = null) where TPixel : struct, IPixel { if (TestEnvironment.RunsOnCI) @@ -102,7 +106,8 @@ namespace SixLabors.ImageSharp.Tests extension, testOutputDetails: testOutputDetails, appendPixelTypeToFileName: appendPixelTypeToFileName, - appendSourceFileOrDescription: appendSourceFileOrDescription); + appendSourceFileOrDescription: appendSourceFileOrDescription, + encoder: encoder); return image; } @@ -255,6 +260,7 @@ namespace SixLabors.ImageSharp.Tests /// A boolean indicating whether we should debug save + compare against a grayscale image, smaller in size. /// A boolean indicating whether to append the pixel type to the output file name. /// A boolean indicating whether to append to the test output file name. + /// A custom decoder. /// public static Image CompareToReferenceOutput( this Image image, @@ -264,7 +270,8 @@ namespace SixLabors.ImageSharp.Tests string extension = "png", bool grayscale = false, bool appendPixelTypeToFileName = true, - bool appendSourceFileOrDescription = true) + bool appendSourceFileOrDescription = true, + IImageDecoder decoder = null) where TPixel : struct, IPixel { using (Image referenceImage = GetReferenceOutputImage( @@ -272,7 +279,8 @@ namespace SixLabors.ImageSharp.Tests testOutputDetails, extension, appendPixelTypeToFileName, - appendSourceFileOrDescription)) + appendSourceFileOrDescription, + decoder)) { comparer.VerifySimilarity(referenceImage, image); } @@ -356,7 +364,8 @@ namespace SixLabors.ImageSharp.Tests object testOutputDetails = null, string extension = "png", bool appendPixelTypeToFileName = true, - bool appendSourceFileOrDescription = true) + bool appendSourceFileOrDescription = true, + IImageDecoder decoder = null) where TPixel : struct, IPixel { string referenceOutputFile = provider.Utility.GetReferenceOutputFileName( @@ -370,7 +379,7 @@ namespace SixLabors.ImageSharp.Tests throw new System.IO.FileNotFoundException("Reference output file missing: " + referenceOutputFile, referenceOutputFile); } - IImageDecoder decoder = TestEnvironment.GetReferenceDecoder(referenceOutputFile); + decoder = decoder ?? TestEnvironment.GetReferenceDecoder(referenceOutputFile); return Image.Load(referenceOutputFile, decoder); } diff --git a/tests/Images/External b/tests/Images/External index d83843deed..dc5afda9a4 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit d83843deedc43712f29f631aab090640b4f54946 +Subproject commit dc5afda9a4ad69dc3526d49ceb1d27610064f2af From 5c2832282576aa866419055758b096bcc0647934 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 12 May 2019 00:52:35 +0200 Subject: [PATCH 170/223] RecolorImageTests --- .../{BeziersTests.cs => DrawBezierTests.cs} | 10 ++-- .../Drawing/RecolorImageTest.cs | 48 ----------------- .../Drawing/RecolorImageTests.cs | 51 +++++++++++++++++++ tests/Images/External | 2 +- 4 files changed, 57 insertions(+), 54 deletions(-) rename tests/ImageSharp.Tests/Drawing/{BeziersTests.cs => DrawBezierTests.cs} (93%) delete mode 100644 tests/ImageSharp.Tests/Drawing/RecolorImageTest.cs create mode 100644 tests/ImageSharp.Tests/Drawing/RecolorImageTests.cs diff --git a/tests/ImageSharp.Tests/Drawing/BeziersTests.cs b/tests/ImageSharp.Tests/Drawing/DrawBezierTests.cs similarity index 93% rename from tests/ImageSharp.Tests/Drawing/BeziersTests.cs rename to tests/ImageSharp.Tests/Drawing/DrawBezierTests.cs index cc8f27f3a2..d82221ca67 100644 --- a/tests/ImageSharp.Tests/Drawing/BeziersTests.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawBezierTests.cs @@ -11,13 +11,13 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Drawing { - public class Beziers : FileTestBase + public class DrawBezierTests { [Fact] public void ImageShouldBeOverlayedByBezierLine() { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "BezierLine"); - using (var image = new Image(500, 500)) + string path = TestEnvironment.CreateOutputDirectory("Drawing", "DrawBezierTests"); + using (var image = new Image(350, 450)) { image.Mutate(x => x.BackgroundColor(Color.Blue)); image.Mutate( @@ -51,11 +51,11 @@ namespace SixLabors.ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedBezierLineWithOpacity() { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "BezierLine"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "DrawBezierTests"); var color = new Rgba32(Rgba32.HotPink.R, Rgba32.HotPink.G, Rgba32.HotPink.B, 150); - using (var image = new Image(500, 500)) + using (var image = new Image(350, 450)) { image.Mutate(x => x.BackgroundColor(Color.Blue)); image.Mutate( diff --git a/tests/ImageSharp.Tests/Drawing/RecolorImageTest.cs b/tests/ImageSharp.Tests/Drawing/RecolorImageTest.cs deleted file mode 100644 index b064d9c964..0000000000 --- a/tests/ImageSharp.Tests/Drawing/RecolorImageTest.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.Primitives; -using Xunit; - -namespace SixLabors.ImageSharp.Tests -{ - public class RecolorImageTest : FileTestBase - { - [Fact] - public void ImageShouldRecolorYellowToHotPink() - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "RecolorImage"); - - var brush = new RecolorBrush(Rgba32.Yellow, Rgba32.HotPink, 0.2f); - - foreach (TestFile file in Files) - { - using (Image image = file.CreateRgba32Image()) - { - image.Mutate(x => x.Fill(brush)); - image.Save($"{path}/{file.FileName}"); - } - } - } - - [Fact] - public void ImageShouldRecolorYellowToHotPinkInARectangle() - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "RecolorImage"); - - var brush = new RecolorBrush(Rgba32.Yellow, Rgba32.HotPink, 0.2f); - - foreach (TestFile file in Files) - { - using (Image image = file.CreateRgba32Image()) - { - int imageHeight = image.Height; - image.Mutate(x => x.Fill(brush, new Rectangle(0, imageHeight / 2 - imageHeight / 4, image.Width, imageHeight / 2))); - image.Save($"{path}/Shaped_{file.FileName}"); - } - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Drawing/RecolorImageTests.cs b/tests/ImageSharp.Tests/Drawing/RecolorImageTests.cs new file mode 100644 index 0000000000..cb671a53d4 --- /dev/null +++ b/tests/ImageSharp.Tests/Drawing/RecolorImageTests.cs @@ -0,0 +1,51 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests +{ + [GroupOutput("Drawing")] + public class RecolorImageTests + { + [Theory] + [WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Rgba32, "Yellow", "Pink", 0.2f)] + [WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Bgra32, "Yellow", "Pink", 0.5f)] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32, "Red", "Blue", 0.2f)] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32, "Red", "Blue", 0.6f)] + public void Recolor(TestImageProvider provider, string sourceColorName, string targetColorName, float threshold) + where TPixel : struct, IPixel + { + Color sourceColor = TestUtils.GetColorByName(sourceColorName); + Color targetColor = TestUtils.GetColorByName(targetColorName); + var brush = new RecolorBrush(sourceColor.ToPixel(), targetColor.ToPixel(), threshold); + + FormattableString testInfo = $"{sourceColorName}-{targetColorName}-{threshold}"; + provider.RunValidatingProcessorTest(x => x.Fill(brush), testInfo); + } + + [Theory] + [WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Bgra32, "Yellow", "Pink", 0.5f)] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32, "Red", "Blue", 0.2f)] + public void Recolor_InBox(TestImageProvider provider, string sourceColorName, string targetColorName, float threshold) + where TPixel : struct, IPixel + { + Color sourceColor = TestUtils.GetColorByName(sourceColorName); + Color targetColor = TestUtils.GetColorByName(targetColorName); + var brush = new RecolorBrush(sourceColor.ToPixel(), targetColor.ToPixel(), threshold); + + FormattableString testInfo = $"{sourceColorName}-{targetColorName}-{threshold}"; + provider.RunValidatingProcessorTest(x => + { + Size size = x.GetCurrentSize(); + var rectangle = new Rectangle(0, size.Height / 2 - size.Height / 4, size.Width, size.Height / 2); + x.Fill(brush, rectangle); + }, testInfo); + } + } +} \ No newline at end of file diff --git a/tests/Images/External b/tests/Images/External index dc5afda9a4..5036d47ec2 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit dc5afda9a4ad69dc3526d49ceb1d27610064f2af +Subproject commit 5036d47ec2393d90efb6f3864d828e2e4381947f From ba92b1c755c27d065572c78de6eac6b679a2a60d Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 12 May 2019 02:34:48 +0200 Subject: [PATCH 171/223] FillPolygonTests --- src/ImageSharp/Color/Color.Conversions.cs | 14 + src/ImageSharp/Color/Color.cs | 12 + tests/ImageSharp.Tests/Color/ColorTests.cs | 11 + .../Drawing/FillPolygonTests.cs | 155 +++++++++++ .../Drawing/RecolorImageTests.cs | 3 +- .../Drawing/SolidPolygonTests.cs | 240 ------------------ tests/Images/External | 2 +- 7 files changed, 195 insertions(+), 242 deletions(-) create mode 100644 tests/ImageSharp.Tests/Drawing/FillPolygonTests.cs delete mode 100644 tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs diff --git a/src/ImageSharp/Color/Color.Conversions.cs b/src/ImageSharp/Color/Color.Conversions.cs index 001aee5a4f..20dca67706 100644 --- a/src/ImageSharp/Color/Color.Conversions.cs +++ b/src/ImageSharp/Color/Color.Conversions.cs @@ -117,6 +117,13 @@ namespace SixLabors.ImageSharp /// The . public static implicit operator Color(Bgr24 source) => new Color(source); + /// + /// Converts an to . + /// + /// The . + /// The . + public static explicit operator Color(Vector4 source) => new Color(source); + /// /// Converts a to . /// @@ -158,5 +165,12 @@ namespace SixLabors.ImageSharp /// The . /// The . public static implicit operator Bgr24(Color color) => color.data.ToBgr24(); + + /// + /// Converts a to . + /// + /// The . + /// The . + public static explicit operator Vector4(Color color) => color.data.ToVector4(); } } \ No newline at end of file diff --git a/src/ImageSharp/Color/Color.cs b/src/ImageSharp/Color/Color.cs index f2f8578d83..fe35033147 100644 --- a/src/ImageSharp/Color/Color.cs +++ b/src/ImageSharp/Color/Color.cs @@ -94,6 +94,18 @@ namespace SixLabors.ImageSharp return new Color(rgba); } + /// + /// Alters the alpha channel of the color, returning a new instance. + /// + /// The new value of alpha [0..1]. + /// The color having it's alpha channel altered. + public Color WithAlpha(float alpha) + { + Vector4 v = (Vector4)this; + v.W = alpha; + return new Color(v); + } + /// /// Gets the hexadecimal representation of the color instance in rrggbbaa form. /// diff --git a/tests/ImageSharp.Tests/Color/ColorTests.cs b/tests/ImageSharp.Tests/Color/ColorTests.cs index 729ef94e3c..e9e22ccdd9 100644 --- a/tests/ImageSharp.Tests/Color/ColorTests.cs +++ b/tests/ImageSharp.Tests/Color/ColorTests.cs @@ -13,6 +13,17 @@ namespace SixLabors.ImageSharp.Tests { public partial class ColorTests { + [Fact] + public void WithAlpha() + { + Color c1 = Color.FromRgba(111, 222, 55, 255); + Color c2 = c1.WithAlpha(0.5f); + + Rgba32 expected = new Rgba32(111, 222, 55, 128); + + Assert.Equal(expected, (Rgba32)c2); + } + [Fact] public void Equality_WhenTrue() { diff --git a/tests/ImageSharp.Tests/Drawing/FillPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/FillPolygonTests.cs new file mode 100644 index 0000000000..43029e6957 --- /dev/null +++ b/tests/ImageSharp.Tests/Drawing/FillPolygonTests.cs @@ -0,0 +1,155 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; + +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.Shapes; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Drawing +{ + [GroupOutput("Drawing")] + public class FillPolygonTests + { + [Theory] + [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "White", 1f, true)] + [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "White", 0.6f, true)] + [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "White", 1f, false)] + [WithBasicTestPatternImages(250, 350, PixelTypes.Bgr24, "Yellow", 1f, true)] + public void FillPolygon_Solid(TestImageProvider provider, string colorName, float alpha, bool antialias) + where TPixel : struct, IPixel + { + SixLabors.Primitives.PointF[] simplePath = + { + new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300) + }; + Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha); + + GraphicsOptions options = new GraphicsOptions(antialias); + + string aa = antialias ? "" : "_NoAntialias"; + FormattableString outputDetails = $"{colorName}_A{alpha}{aa}"; + + provider.RunValidatingProcessorTest( + c => c.FillPolygon(options, color.ToPixel(), simplePath), + outputDetails, + appendSourceFileOrDescription: false); + } + + [Theory] + [WithBasicTestPatternImages(200, 200, PixelTypes.Rgba32)] + public void FillPolygon_Concave(TestImageProvider provider) + where TPixel : struct, IPixel + { + var points = new SixLabors.Primitives.PointF[] + { + new Vector2(8, 8), + new Vector2(64, 8), + new Vector2(64, 64), + new Vector2(120, 64), + new Vector2(120, 120), + new Vector2(8, 120) + }; + + Color color = Color.LightGreen; + + provider.RunValidatingProcessorTest( + c => c.FillPolygon(color.ToPixel(), points), + appendSourceFileOrDescription: false, + appendPixelTypeToFileName: false); + } + + [Theory] + [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32)] + public void FillPolygon_Pattern(TestImageProvider provider) + where TPixel : struct, IPixel + { + SixLabors.Primitives.PointF[] simplePath = + { + new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300) + }; + Color color = Color.Yellow; + + var brush = Brushes.Horizontal(color.ToPixel()); + + provider.RunValidatingProcessorTest( + c => c.FillPolygon(brush, simplePath), + appendSourceFileOrDescription: false); + } + + [Theory] + [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, TestImages.Png.Ducky)] + [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, TestImages.Bmp.Car)] + public void FillPolygon_ImageBrush(TestImageProvider provider, string brushImageName) + where TPixel : struct, IPixel + { + SixLabors.Primitives.PointF[] simplePath = + { + new Vector2(10, 10), new Vector2(200, 50), new Vector2(50, 200) + }; + + using (Image brushImage = Image.Load(TestFile.Create(brushImageName).Bytes)) + { + var brush = new ImageBrush(brushImage); + + provider.RunValidatingProcessorTest( + c => c.FillPolygon(brush, simplePath), + System.IO.Path.GetFileNameWithoutExtension(brushImageName), + appendSourceFileOrDescription: false); + } + } + + [Theory] + [WithBasicTestPatternImages(250, 250, PixelTypes.Rgba32)] + public void Fill_RectangularPolygon(TestImageProvider provider) + where TPixel : struct, IPixel + { + var polygon = new SixLabors.Shapes.RectangularPolygon(10, 10, 190, 140); + Color color = Color.White; + + provider.RunValidatingProcessorTest( + c => c.Fill(color.ToPixel(), polygon), + appendSourceFileOrDescription: false); + } + + [Theory] + [WithBasicTestPatternImages(200, 200, PixelTypes.Rgba32, 3, 50, 0f)] + [WithBasicTestPatternImages(200, 200, PixelTypes.Rgba32, 3, 60, 20f)] + [WithBasicTestPatternImages(200, 200, PixelTypes.Rgba32, 3, 60, -180f)] + [WithBasicTestPatternImages(200, 200, PixelTypes.Rgba32, 5, 70, 0f)] + [WithBasicTestPatternImages(200, 200, PixelTypes.Rgba32, 7, 80, -180f)] + public void Fill_RegularPolygon(TestImageProvider provider, int vertices, float radius, float angleDeg) + where TPixel : struct, IPixel + { + float angle = GeometryUtilities.DegreeToRadian(angleDeg); + var polygon = new RegularPolygon(100, 100, vertices, radius, angle); + Color color = Color.Yellow; + + FormattableString testOutput = $"V({vertices})_R({radius})_Ang({angleDeg})"; + provider.RunValidatingProcessorTest( + c => c.Fill(color.ToPixel(), polygon), + testOutput, + appendSourceFileOrDescription: false, + appendPixelTypeToFileName: false); + } + + [Theory] + [WithBasicTestPatternImages(200, 200, PixelTypes.Rgba32)] + public void Fill_EllipsePolygon(TestImageProvider provider) + where TPixel : struct, IPixel + { + var polygon = new EllipsePolygon(100, 100, 80, 120); + Color color = Color.Azure; + + provider.RunValidatingProcessorTest( + c => c.Fill(color.ToPixel(), polygon), + appendSourceFileOrDescription: false, + appendPixelTypeToFileName: false); + } + } +} diff --git a/tests/ImageSharp.Tests/Drawing/RecolorImageTests.cs b/tests/ImageSharp.Tests/Drawing/RecolorImageTests.cs index cb671a53d4..461ca700f7 100644 --- a/tests/ImageSharp.Tests/Drawing/RecolorImageTests.cs +++ b/tests/ImageSharp.Tests/Drawing/RecolorImageTests.cs @@ -6,9 +6,10 @@ using System; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.Primitives; + using Xunit; -namespace SixLabors.ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests.Drawing { [GroupOutput("Drawing")] public class RecolorImageTests diff --git a/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs deleted file mode 100644 index 6a299aad7b..0000000000 --- a/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs +++ /dev/null @@ -1,240 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Numerics; - -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.Shapes; - -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Drawing -{ - public class SolidPolygonTests : FileTestBase - { - [Fact] - public void ImageShouldBeOverlayedByFilledPolygon() - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons"); - SixLabors.Primitives.PointF[] simplePath = { - new Vector2(10, 10), - new Vector2(200, 150), - new Vector2(50, 300) - }; - - using (var image = new Image(500, 500)) - { - image.Mutate(x => x.FillPolygon(new GraphicsOptions(true), Rgba32.HotPink, simplePath)); - image.Save($"{path}/Simple.png"); - - Buffer2D sourcePixels = image.GetRootFramePixelBuffer(); - Assert.Equal(Rgba32.HotPink, sourcePixels[81, 145]); - } - } - - [Fact] - public void ImageShouldBeOverlayedByFilledPolygonWithPattern() - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons"); - var simplePath = new SixLabors.Primitives.PointF[] { - new Vector2(10, 10), - new Vector2(200, 150), - new Vector2(50, 300) - }; - - using (var image = new Image(500, 500)) - { - image.Mutate( - x => x.FillPolygon(new GraphicsOptions(true), Brushes.Horizontal(Rgba32.HotPink), simplePath)); - image.Save($"{path}/Pattern.png"); - - Buffer2D sourcePixels = image.GetRootFramePixelBuffer(); - Assert.Equal(Rgba32.HotPink, sourcePixels[81, 145]); - } - } - - [Fact] - public void ImageShouldBeOverlayedByFilledPolygonNoAntialias() - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons"); - var simplePath = new SixLabors.Primitives.PointF[] { - new Vector2(10, 10), - new Vector2(200, 150), - new Vector2(50, 300) - }; - - using (var image = new Image(500, 500)) - { - image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); - image.Mutate( - x => x.FillPolygon( - new GraphicsOptions(false), - Rgba32.HotPink, - simplePath)); - image.Save($"{path}/Simple_NoAntialias.png"); - - Buffer2D sourcePixels = image.GetRootFramePixelBuffer(); - Assert.True(Rgba32.HotPink == sourcePixels[11, 11], "[11, 11] wrong"); - - Assert.True(Rgba32.HotPink == sourcePixels[199, 149], "[199, 149] wrong"); - - Assert.True(Rgba32.HotPink == sourcePixels[50, 50], "[50, 50] wrong"); - - Assert.True(Rgba32.Blue == sourcePixels[2, 2], "[2, 2] wrong"); - } - } - - [Fact] - public void ImageShouldBeOverlayedByFilledPolygonImage() - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons"); - var simplePath = new SixLabors.Primitives.PointF[] { - new Vector2(10, 10), - new Vector2(200, 150), - new Vector2(50, 300) - }; - - using (Image brushImage = TestFile.Create(TestImages.Bmp.Car).CreateRgba32Image()) - using (var image = new Image(500, 500)) - { - var brush = new ImageBrush(brushImage); - - image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); - image.Mutate(x => x.FillPolygon(brush, simplePath)); - image.Save($"{path}/Image.png"); - } - } - - [Fact] - public void ImageShouldBeOverlayedByFilledPolygonOpacity() - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons"); - var simplePath = new SixLabors.Primitives.PointF[] { - new Vector2(10, 10), - new Vector2(200, 150), - new Vector2(50, 300) - }; - var color = new Rgba32(Rgba32.HotPink.R, Rgba32.HotPink.G, Rgba32.HotPink.B, 150); - - using (var image = new Image(500, 500)) - { - image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); - image.Mutate(x => x.FillPolygon(color, simplePath)); - image.Save($"{path}/Opacity.png"); - - //shift background color towards forground color by the opacity amount - var mergedColor = new Rgba32( - Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f)); - - Buffer2D sourcePixels = image.GetRootFramePixelBuffer(); - Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]); - } - } - - [Fact] - public void ImageShouldBeOverlayedByFilledRectangle() - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons"); - - using (var image = new Image(500, 500)) - { - image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); - image.Mutate( - x => x.Fill( - Rgba32.HotPink, - new SixLabors.Shapes.RectangularPolygon(10, 10, 190, 140))); - image.Save($"{path}/Rectangle.png"); - - Buffer2D sourcePixels = image.GetRootFramePixelBuffer(); - Assert.Equal(Rgba32.HotPink, sourcePixels[11, 11]); - - Assert.Equal(Rgba32.HotPink, sourcePixels[198, 10]); - - Assert.Equal(Rgba32.HotPink, sourcePixels[10, 50]); - - Assert.Equal(Rgba32.HotPink, sourcePixels[50, 50]); - - Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]); - } - } - - [Fact] - public void ImageShouldBeOverlayedByFilledTriangle() - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons"); - - using (var image = new Image(100, 100)) - { - image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); - image.Mutate( - x => x.Fill(Rgba32.HotPink, new RegularPolygon(50, 50, 3, 30))); - image.Save($"{path}/Triangle.png"); - - Buffer2D sourcePixels = image.GetRootFramePixelBuffer(); - Assert.Equal(Rgba32.Blue, sourcePixels[30, 65]); - - Assert.Equal(Rgba32.HotPink, sourcePixels[50, 50]); - } - } - - [Fact] - public void ImageShouldBeOverlayedByFilledSeptagon() - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons"); - - var config = Configuration.CreateDefaultInstance(); - config.MaxDegreeOfParallelism = 1; - using (var image = new Image(config, 100, 100)) - { - image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); - image.Mutate(x => x.Fill(Rgba32.HotPink, - new RegularPolygon(50, 50, 7, 30, -(float)Math.PI))); - image.Save($"{path}/Septagon.png"); - } - } - - [Fact] - public void ImageShouldBeOverlayedByFilledEllipse() - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons"); - - var config = Configuration.CreateDefaultInstance(); - config.MaxDegreeOfParallelism = 1; - using (var image = new Image(config, 100, 100)) - { - image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); - image.Mutate(x => x - .Fill(Rgba32.HotPink, new EllipsePolygon(50, 50, 30, 50) - .Rotate((float)(Math.PI / 3)))); - image.Save($"{path}/ellipse.png"); - } - } - - [Fact] - public void ImageShouldBeOverlayedBySquareWithCornerClipped() - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons"); - - var config = Configuration.CreateDefaultInstance(); - config.MaxDegreeOfParallelism = 1; - using (var image = new Image(config, 200, 200)) - { - image.Mutate(x => x - .Fill(Rgba32.Blue) - .FillPolygon(Rgba32.HotPink, new SixLabors.Primitives.PointF[] - { - new Vector2( 8, 8 ), - new Vector2( 64, 8 ), - new Vector2( 64, 64 ), - new Vector2( 120, 64 ), - new Vector2( 120, 120 ), - new Vector2( 8, 120 ) - })); - image.Save($"{path}/clipped-corner.png"); - } - } - } -} diff --git a/tests/Images/External b/tests/Images/External index 5036d47ec2..0edeb078b9 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 5036d47ec2393d90efb6f3864d828e2e4381947f +Subproject commit 0edeb078b9d9f9c8be016ca514a3a625c4768bd6 From 012ae224556b6841d2e5420c03aeb7af50cef5f1 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 12 May 2019 03:00:05 +0200 Subject: [PATCH 172/223] DrawPolygonTests, DrawLinesTests --- .../Drawing/DrawLinesTests.cs | 100 +++++++++ .../Drawing/DrawPolygonTests.cs | 43 ++++ tests/ImageSharp.Tests/Drawing/LineTests.cs | 193 ------------------ .../ImageSharp.Tests/Drawing/PolygonTests.cs | 102 --------- tests/Images/External | 2 +- 5 files changed, 144 insertions(+), 296 deletions(-) create mode 100644 tests/ImageSharp.Tests/Drawing/DrawLinesTests.cs create mode 100644 tests/ImageSharp.Tests/Drawing/DrawPolygonTests.cs delete mode 100644 tests/ImageSharp.Tests/Drawing/LineTests.cs delete mode 100644 tests/ImageSharp.Tests/Drawing/PolygonTests.cs diff --git a/tests/ImageSharp.Tests/Drawing/DrawLinesTests.cs b/tests/ImageSharp.Tests/Drawing/DrawLinesTests.cs new file mode 100644 index 0000000000..c225b921e6 --- /dev/null +++ b/tests/ImageSharp.Tests/Drawing/DrawLinesTests.cs @@ -0,0 +1,100 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; + +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Drawing +{ + [GroupOutput("Drawing")] + public class DrawLinesTests + { + [Theory] + [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "White", 1f, 2.5, true)] + [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "White", 0.6f, 10, true)] + [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "White", 1f, 5, false)] + [WithBasicTestPatternImages(250, 350, PixelTypes.Bgr24, "Yellow", 1f, 10, true)] + public void DrawLines_Simple(TestImageProvider provider, string colorName, float alpha, float thickness, bool antialias) + where TPixel : struct, IPixel + { + Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha); + Pen pen = new Pen(color.ToPixel(), thickness); + + DrawLinesImpl(provider, colorName, alpha, thickness, antialias, pen); + } + + [Theory] + [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "White", 1f, 5, false)] + public void DrawLines_Dash(TestImageProvider provider, string colorName, float alpha, float thickness, bool antialias) + where TPixel : struct, IPixel + { + Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha); + Pen pen = Pens.Dash(color.ToPixel(), thickness); + + DrawLinesImpl(provider, colorName, alpha, thickness, antialias, pen); + } + + [Theory] + [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "LightGreen", 1f, 5, false)] + public void DrawLines_Dot(TestImageProvider provider, string colorName, float alpha, float thickness, bool antialias) + where TPixel : struct, IPixel + { + Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha); + Pen pen = Pens.Dot(color.ToPixel(), thickness); + + DrawLinesImpl(provider, colorName, alpha, thickness, antialias, pen); + } + + [Theory] + [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "Yellow", 1f, 5, false)] + public void DrawLines_DashDot(TestImageProvider provider, string colorName, float alpha, float thickness, bool antialias) + where TPixel : struct, IPixel + { + Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha); + Pen pen = Pens.DashDot(color.ToPixel(), thickness); + + DrawLinesImpl(provider, colorName, alpha, thickness, antialias, pen); + } + + [Theory] + [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "Black", 1f, 5, false)] + public void DrawLines_DashDotDot(TestImageProvider provider, string colorName, float alpha, float thickness, bool antialias) + where TPixel : struct, IPixel + { + Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha); + Pen pen = Pens.DashDotDot(color.ToPixel(), thickness); + + DrawLinesImpl(provider, colorName, alpha, thickness, antialias, pen); + } + + + private static void DrawLinesImpl( + TestImageProvider provider, + string colorName, + float alpha, + float thickness, + bool antialias, + Pen pen) + where TPixel : struct, IPixel + { + SixLabors.Primitives.PointF[] simplePath = { new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300) }; + + GraphicsOptions options = new GraphicsOptions(antialias); + + string aa = antialias ? "" : "_NoAntialias"; + FormattableString outputDetails = $"{colorName}_A({alpha})_T({thickness}){aa}"; + + provider.RunValidatingProcessorTest( + c => c.DrawLines(options, pen, simplePath), + outputDetails, + appendSourceFileOrDescription: false); + } + + } +} diff --git a/tests/ImageSharp.Tests/Drawing/DrawPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/DrawPolygonTests.cs new file mode 100644 index 0000000000..8a0f256670 --- /dev/null +++ b/tests/ImageSharp.Tests/Drawing/DrawPolygonTests.cs @@ -0,0 +1,43 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; + +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +using SixLabors.ImageSharp.Processing; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Drawing +{ + [GroupOutput("Drawing")] + public class DrawPolygonTests + { + [Theory] + [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "White", 1f, 2.5, true)] + [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "White", 0.6f, 10, true)] + [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "White", 1f, 5, false)] + [WithBasicTestPatternImages(250, 350, PixelTypes.Bgr24, "Yellow", 1f, 10, true)] + public void DrawPolygon(TestImageProvider provider, string colorName, float alpha, float thickness, bool antialias) + where TPixel : struct, IPixel + { + SixLabors.Primitives.PointF[] simplePath = + { + new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300) + }; + Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha); + + GraphicsOptions options = new GraphicsOptions(antialias); + + string aa = antialias ? "" : "_NoAntialias"; + FormattableString outputDetails = $"{colorName}_A({alpha})_T({thickness}){aa}"; + + provider.RunValidatingProcessorTest( + c => c.DrawPolygon(options, color.ToPixel(), thickness, simplePath), + outputDetails, + appendSourceFileOrDescription: false); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Drawing/LineTests.cs b/tests/ImageSharp.Tests/Drawing/LineTests.cs deleted file mode 100644 index 1cc0fd1a40..0000000000 --- a/tests/ImageSharp.Tests/Drawing/LineTests.cs +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Numerics; - -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; - -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Drawing -{ - public class LineTests : FileTestBase - { - [Fact] - public void ImageShouldBeOverlayedByPath() - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "Lines"); - using (var image = new Image(500, 500)) - { - image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); - image.Mutate( - x => x.DrawLines( - Rgba32.HotPink, - 5, - new Vector2(10, 10), - new Vector2(200, 150), - new Vector2(50, 300))); - image.Save($"{path}/Simple.png"); - - Buffer2D sourcePixels = image.GetRootFramePixelBuffer(); - Assert.Equal(Rgba32.HotPink, sourcePixels[11, 11]); - - Assert.Equal(Rgba32.HotPink, sourcePixels[199, 149]); - - Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]); - } - } - - [Fact] - public void ImageShouldBeOverlayedByPath_NoAntialias() - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "Lines"); - using (var image = new Image(500, 500)) - { - image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); - image.Mutate( - x => x.DrawLines( - new GraphicsOptions(false), - Rgba32.HotPink, - 5, - new Vector2(10, 10), - new Vector2(200, 150), - new Vector2(50, 300))); - image.Save($"{path}/Simple_noantialias.png"); - - Buffer2D sourcePixels = image.GetRootFramePixelBuffer(); - Assert.Equal(Rgba32.HotPink, sourcePixels[11, 11]); - - Assert.Equal(Rgba32.HotPink, sourcePixels[199, 149]); - - Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]); - } - } - - [Fact] - public void ImageShouldBeOverlayedByPathDashed() - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "Lines"); - using (var image = new Image(500, 500)) - { - image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); - image.Mutate(x => x.DrawLines(Pens.Dash(Rgba32.HotPink, 5), - new SixLabors.Primitives.PointF[] { - new Vector2(10, 10), - new Vector2(200, 150), - new Vector2(50, 300) - })); - image.Save($"{path}/Dashed.png"); - } - } - - [Fact] - public void ImageShouldBeOverlayedByPathDotted() - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "Lines"); - using (var image = new Image(500, 500)) - { - image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); - image.Mutate(x => x.DrawLines(Pens.Dot(Rgba32.HotPink, 5), - new SixLabors.Primitives.PointF[] { - new Vector2(10, 10), - new Vector2(200, 150), - new Vector2(50, 300) - })); - image.Save($"{path}/Dot.png"); - } - } - - [Fact] - public void ImageShouldBeOverlayedByPathDashDot() - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "Lines"); - using (var image = new Image(500, 500)) - { - image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); - image.Mutate(x => x.DrawLines(Pens.DashDot(Rgba32.HotPink, 5), - new SixLabors.Primitives.PointF[] { - new Vector2(10, 10), - new Vector2(200, 150), - new Vector2(50, 300) - })); - image.Save($"{path}/DashDot.png"); - } - } - - [Fact] - public void ImageShouldBeOverlayedByPathDashDotDot() - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "Lines"); - var image = new Image(500, 500); - - image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); - image.Mutate(x => x.DrawLines(Pens.DashDotDot(Rgba32.HotPink, 5), - new SixLabors.Primitives.PointF[] { - new Vector2(10, 10), - new Vector2(200, 150), - new Vector2(50, 300) - })); - image.Save($"{path}/DashDotDot.png"); - } - - [Fact] - public void ImageShouldBeOverlayedPathWithOpacity() - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "Lines"); - - var color = new Rgba32(Rgba32.HotPink.R, Rgba32.HotPink.G, Rgba32.HotPink.B, 150); - - var image = new Image(500, 500); - - image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); - image.Mutate( - x => x.DrawLines( - color, - 10, - new Vector2(10, 10), - new Vector2(200, 150), - new Vector2(50, 300))); - image.Save($"{path}/Opacity.png"); - - //shift background color towards forground color by the opacity amount - var mergedColor = - new Rgba32(Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f)); - - Buffer2D sourcePixels = image.GetRootFramePixelBuffer(); - Assert.Equal(mergedColor, sourcePixels[11, 11]); - - Assert.Equal(mergedColor, sourcePixels[199, 149]); - - Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]); - } - - [Fact] - public void ImageShouldBeOverlayedByPathOutline() - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "Lines"); - - var image = new Image(500, 500); - - image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); - image.Mutate( - x => x.DrawLines( - Rgba32.HotPink, - 10, - new Vector2(10, 10), - new Vector2(200, 10), - new Vector2(200, 150), - new Vector2(10, 150))); - image.Save($"{path}/Rectangle.png"); - - Buffer2D sourcePixels = image.GetRootFramePixelBuffer(); - Assert.Equal(Rgba32.HotPink, sourcePixels[11, 11]); - - Assert.Equal(Rgba32.HotPink, sourcePixels[198, 10]); - - Assert.Equal(Rgba32.Blue, sourcePixels[10, 50]); - - Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]); - } - } -} diff --git a/tests/ImageSharp.Tests/Drawing/PolygonTests.cs b/tests/ImageSharp.Tests/Drawing/PolygonTests.cs deleted file mode 100644 index febb39c20d..0000000000 --- a/tests/ImageSharp.Tests/Drawing/PolygonTests.cs +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Numerics; - -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; -using SixLabors.ImageSharp.Processing; -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Drawing -{ - public class PolygonTests : FileTestBase - { - [Fact] - public void ImageShouldBeOverlayedByPolygonOutline() - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "Polygons"); - - using (Image image = new Image(500, 500)) - { - image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); - image.Mutate( - x => x.DrawPolygon( - Rgba32.HotPink, - 5, - new Vector2(10, 10), - new Vector2(200, 150), - new Vector2(50, 300))); - image.Save($"{path}/Simple.png"); - - Buffer2D sourcePixels = image.GetRootFramePixelBuffer(); - Assert.Equal(Rgba32.HotPink, sourcePixels[9, 9]); - - Assert.Equal(Rgba32.HotPink, sourcePixels[199, 149]); - - Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]); - - Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]); - } - } - - [Fact] - public void ImageShouldBeOverlayedPolygonOutlineWithOpacity() - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "Polygons"); - PointF[] simplePath = { - new Vector2(10, 10), - new Vector2(200, 150), - new Vector2(50, 300) - }; - - Rgba32 color = new Rgba32(Rgba32.HotPink.R, Rgba32.HotPink.G, Rgba32.HotPink.B, 150); - - using (Image image = new Image(500, 500)) - { - image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); - image.Mutate(x => x.DrawPolygon(color, 10, simplePath)); - image.Save($"{path}/Opacity.png"); - - //shift background color towards forground color by the opacity amount - Rgba32 mergedColor = new Rgba32( - Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f)); - - Buffer2D sourcePixels = image.GetRootFramePixelBuffer(); - Assert.Equal(mergedColor, sourcePixels[9, 9]); - - Assert.Equal(mergedColor, sourcePixels[199, 149]); - - Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]); - - Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]); - } - } - - [Fact] - public void ImageShouldBeOverlayedByRectangleOutline() - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "Polygons"); - - using (Image image = new Image(500, 500)) - { - image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); - image.Mutate( - x => x.Draw(Rgba32.HotPink, 10, new Rectangle(10, 10, 190, 140))); - image.Save($"{path}/Rectangle.png"); - - Buffer2D sourcePixels = image.GetRootFramePixelBuffer(); - Assert.Equal(Rgba32.HotPink, sourcePixels[8, 8]); - - Assert.Equal(Rgba32.HotPink, sourcePixels[198, 10]); - - Assert.Equal(Rgba32.HotPink, sourcePixels[10, 50]); - - Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]); - - Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]); - } - } - } -} \ No newline at end of file diff --git a/tests/Images/External b/tests/Images/External index 0edeb078b9..4fe9334aa8 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 0edeb078b9d9f9c8be016ca514a3a625c4768bd6 +Subproject commit 4fe9334aa8898f67b6e3df94bc00867645c3e709 From 70772214f54cc2e54e11e8f5865d8a1c777b662a Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 12 May 2019 03:22:21 +0200 Subject: [PATCH 173/223] DrawBeziersTests, DrawComplexPolygonTests --- .../Drawing/DrawBezierTests.cs | 102 +++------ .../Drawing/DrawComplexPolygonTests.cs | 70 ++++++ .../Drawing/LineComplexPolygonTests.cs | 201 ------------------ tests/Images/External | 2 +- 4 files changed, 100 insertions(+), 275 deletions(-) create mode 100644 tests/ImageSharp.Tests/Drawing/DrawComplexPolygonTests.cs delete mode 100644 tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs diff --git a/tests/ImageSharp.Tests/Drawing/DrawBezierTests.cs b/tests/ImageSharp.Tests/Drawing/DrawBezierTests.cs index d82221ca67..0f29728c9f 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawBezierTests.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawBezierTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; using System.Numerics; using SixLabors.ImageSharp.Memory; @@ -11,82 +12,37 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Drawing { + [GroupOutput("Drawing")] public class DrawBezierTests { - [Fact] - public void ImageShouldBeOverlayedByBezierLine() + public static readonly TheoryData DrawPathData = new TheoryData + { + { "White", 255, 1.5f }, + { "Red", 255, 3 }, + { "HotPink", 255, 5 }, + { "HotPink", 150, 5 }, + { "White", 255, 15 }, + }; + + [Theory] + [WithSolidFilledImages(nameof(DrawPathData), 300, 450, "Blue", PixelTypes.Rgba32)] + public void DrawBeziers(TestImageProvider provider, string colorName, byte alpha, float thickness) + where TPixel : struct, IPixel { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "DrawBezierTests"); - using (var image = new Image(350, 450)) - { - image.Mutate(x => x.BackgroundColor(Color.Blue)); - image.Mutate( - x => x.DrawBeziers( - Rgba32.HotPink, - 5, - new SixLabors.Primitives.PointF[] - { - new Vector2(10, 400), new Vector2(30, 10), new Vector2(240, 30), new Vector2(300, 400) - })); - image.Save($"{path}/Simple.png"); - - Buffer2D sourcePixels = image.GetRootFramePixelBuffer(); - //top of curve - Assert.Equal(Rgba32.HotPink, sourcePixels[138, 115]); - - //start points - Assert.Equal(Rgba32.HotPink, sourcePixels[10, 395]); - Assert.Equal(Rgba32.HotPink, sourcePixels[300, 395]); - - //curve points should not be never be set - Assert.Equal(Rgba32.Blue, sourcePixels[30, 10]); - Assert.Equal(Rgba32.Blue, sourcePixels[240, 30]); - - // inside shape should be empty - Assert.Equal(Rgba32.Blue, sourcePixels[200, 250]); - } - } - - - [Fact] - public void ImageShouldBeOverlayedBezierLineWithOpacity() - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "DrawBezierTests"); - - var color = new Rgba32(Rgba32.HotPink.R, Rgba32.HotPink.G, Rgba32.HotPink.B, 150); - - using (var image = new Image(350, 450)) - { - image.Mutate(x => x.BackgroundColor(Color.Blue)); - image.Mutate( - x => x.DrawBeziers( - color, - 10, - new SixLabors.Primitives.PointF[] - { - new Vector2(10, 400), new Vector2(30, 10), new Vector2(240, 30), new Vector2(300, 400) - })); - image.Save($"{path}/Opacity.png"); - - //shift background color towards foreground color by the opacity amount - var mergedColor = new Rgba32( - Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f)); - - Buffer2D sourcePixels = image.GetRootFramePixelBuffer(); - // top of curve - Assert.Equal(mergedColor, sourcePixels[138, 115]); - - // start points - Assert.Equal(mergedColor, sourcePixels[10, 395]); - Assert.Equal(mergedColor, sourcePixels[300, 395]); - - // curve points should not be never be set - Assert.Equal(Rgba32.Blue, sourcePixels[30, 10]); - Assert.Equal(Rgba32.Blue, sourcePixels[240, 30]); - - // inside shape should be empty - Assert.Equal(Rgba32.Blue, sourcePixels[200, 250]); - } + var points = new SixLabors.Primitives.PointF[] + { + new Vector2(10, 400), new Vector2(30, 10), new Vector2(240, 30), new Vector2(300, 400) + }; + Rgba32 rgba = TestUtils.GetColorByName(colorName); + rgba.A = alpha; + Color color = rgba; + + FormattableString testDetails = $"{colorName}_A{alpha}_T{thickness}"; + + provider.RunValidatingProcessorTest( x => x.DrawBeziers(color.ToPixel(), 5f, points), + testDetails, + appendSourceFileOrDescription: false, + appendPixelTypeToFileName: false); } } } diff --git a/tests/ImageSharp.Tests/Drawing/DrawComplexPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/DrawComplexPolygonTests.cs new file mode 100644 index 0000000000..b6e105a199 --- /dev/null +++ b/tests/ImageSharp.Tests/Drawing/DrawComplexPolygonTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; + +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.Shapes; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Drawing +{ + [GroupOutput("Drawing")] + public class DrawComplexPolygonTests + { + [Theory] + [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, false, false, false)] + [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, true, false, false)] + [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, false, true, false)] + [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, false, false, true)] + public void DrawComplexPolygon(TestImageProvider provider, bool overlap, bool transparent, bool dashed) + where TPixel :struct, IPixel + { + var simplePath = new Polygon(new LinearLineSegment( + new Vector2(10, 10), + new Vector2(200, 150), + new Vector2(50, 300))); + + var hole1 = new Polygon(new LinearLineSegment( + new Vector2(37, 85), + overlap ? new Vector2(130, 40) : new Vector2(93, 85), + new Vector2(65, 137))); + IPath clipped = simplePath.Clip(hole1); + + Rgba32 colorRgba = Rgba32.White; + if (transparent) + { + colorRgba.A = 150; + } + + Color color = colorRgba; + + string testDetails = ""; + if (overlap) + { + testDetails += "_Overlap"; + } + + if (transparent) + { + testDetails += "_Transparent"; + } + + if (dashed) + { + testDetails += "_Dashed"; + } + + Pen pen = dashed ? Pens.Dash(color.ToPixel(), 5f) : Pens.Solid(color.ToPixel(), 5f); + + provider.RunValidatingProcessorTest( + x => x.Draw(pen, clipped), + testDetails, + appendPixelTypeToFileName: false, + appendSourceFileOrDescription: false); + } + } +} diff --git a/tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs deleted file mode 100644 index 06961bec4b..0000000000 --- a/tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs +++ /dev/null @@ -1,201 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Numerics; - -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.Shapes; - -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Drawing -{ - public class LineComplexPolygonTests : FileTestBase - { - [Fact] - public void ImageShouldBeOverlayedByPolygonOutline() - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "LineComplexPolygon"); - - var simplePath = new Polygon(new LinearLineSegment( - new Vector2(10, 10), - new Vector2(200, 150), - new Vector2(50, 300))); - - var hole1 = new Polygon(new LinearLineSegment( - new Vector2(37, 85), - new Vector2(93, 85), - new Vector2(65, 137))); - - using (var image = new Image(500, 500)) - { - image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); - image.Mutate(x => x.Draw(Rgba32.HotPink, 5, simplePath.Clip(hole1))); - image.Save($"{path}/Simple.png"); - - Buffer2D sourcePixels = image.GetRootFramePixelBuffer(); - Assert.Equal(Rgba32.HotPink, sourcePixels[10, 10]); - - Assert.Equal(Rgba32.HotPink, sourcePixels[200, 150]); - - Assert.Equal(Rgba32.HotPink, sourcePixels[50, 300]); - - Assert.Equal(Rgba32.HotPink, sourcePixels[37, 85]); - - Assert.Equal(Rgba32.HotPink, sourcePixels[93, 85]); - - Assert.Equal(Rgba32.HotPink, sourcePixels[65, 137]); - - Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]); - - //inside hole - Assert.Equal(Rgba32.Blue, sourcePixels[57, 99]); - - //inside shape - Assert.Equal(Rgba32.Blue, sourcePixels[100, 192]); - } - } - - [Fact] - public void ImageShouldBeOverlayedByPolygonOutlineNoOverlapping() - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "LineComplexPolygon"); - var simplePath = new Polygon(new LinearLineSegment( - new Vector2(10, 10), - new Vector2(200, 150), - new Vector2(50, 300))); - - var hole1 = new Polygon(new LinearLineSegment( - new Vector2(207, 25), - new Vector2(263, 25), - new Vector2(235, 57))); - - using (var image = new Image(500, 500)) - { - image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); - image.Mutate(x => x.Draw(Rgba32.HotPink, 5, simplePath.Clip(hole1))); - image.Save($"{path}/SimpleVanishHole.png"); - - Buffer2D sourcePixels = image.GetRootFramePixelBuffer(); - Assert.Equal(Rgba32.HotPink, sourcePixels[10, 10]); - Assert.Equal(Rgba32.HotPink, sourcePixels[200, 150]); - Assert.Equal(Rgba32.HotPink, sourcePixels[50, 300]); - - //Assert.Equal(Color.HotPink, sourcePixels[37, 85]); - - //Assert.Equal(Color.HotPink, sourcePixels[93, 85]); - - //Assert.Equal(Color.HotPink, sourcePixels[65, 137]); - - Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]); - - //inside hole - Assert.Equal(Rgba32.Blue, sourcePixels[57, 99]); - - //inside shape - Assert.Equal(Rgba32.Blue, sourcePixels[100, 192]); - } - } - - [Fact] - public void ImageShouldBeOverlayedByPolygonOutlineOverlapping() - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "LineComplexPolygon"); - var simplePath = new Polygon(new LinearLineSegment( - new Vector2(10, 10), - new Vector2(200, 150), - new Vector2(50, 300))); - - var hole1 = new Polygon(new LinearLineSegment( - new Vector2(37, 85), - new Vector2(130, 40), - new Vector2(65, 137))); - - using (var image = new Image(500, 500)) - { - image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); - image.Mutate(x => x.Draw(Rgba32.HotPink, 5, simplePath.Clip(hole1))); - image.Save($"{path}/SimpleOverlapping.png"); - - Buffer2D sourcePixels = image.GetRootFramePixelBuffer(); - Assert.Equal(Rgba32.HotPink, sourcePixels[10, 10]); - Assert.Equal(Rgba32.HotPink, sourcePixels[200, 150]); - Assert.Equal(Rgba32.HotPink, sourcePixels[50, 300]); - Assert.Equal(Rgba32.Blue, sourcePixels[130, 41]); - Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]); - - //inside hole - Assert.Equal(Rgba32.Blue, sourcePixels[57, 99]); - - //inside shape - Assert.Equal(Rgba32.Blue, sourcePixels[100, 192]); - } - } - - [Fact] - public void ImageShouldBeOverlayedByPolygonOutlineDashed() - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "LineComplexPolygon"); - var simplePath = new Polygon(new LinearLineSegment( - new Vector2(10, 10), - new Vector2(200, 150), - new Vector2(50, 300))); - - var hole1 = new Polygon(new LinearLineSegment( - new Vector2(37, 85), - new Vector2(93, 85), - new Vector2(65, 137))); - - using (var image = new Image(500, 500)) - { - image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); - image.Mutate(x => x.Draw(Pens.Dash(Rgba32.HotPink, 5), simplePath.Clip(hole1))); - image.Save($"{path}/Dashed.png"); - } - } - - [Fact] - public void ImageShouldBeOverlayedPolygonOutlineWithOpacity() - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "LineComplexPolygon"); - var simplePath = new Polygon(new LinearLineSegment( - new Vector2(10, 10), - new Vector2(200, 150), - new Vector2(50, 300))); - - var hole1 = new Polygon(new LinearLineSegment( - new Vector2(37, 85), - new Vector2(93, 85), - new Vector2(65, 137))); - var color = new Rgba32(Rgba32.HotPink.R, Rgba32.HotPink.G, Rgba32.HotPink.B, 150); - - using (var image = new Image(500, 500)) - { - image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); - image.Mutate(x => x.Draw(color, 5, simplePath.Clip(hole1))); - image.Save($"{path}/Opacity.png"); - - //shift background color towards forground color by the opacity amount - var mergedColor = new Rgba32( - Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f)); - - Buffer2D sourcePixels = image.GetRootFramePixelBuffer(); - Assert.Equal(mergedColor, sourcePixels[10, 10]); - Assert.Equal(mergedColor, sourcePixels[200, 150]); - Assert.Equal(mergedColor, sourcePixels[50, 300]); - Assert.Equal(mergedColor, sourcePixels[37, 85]); - Assert.Equal(mergedColor, sourcePixels[93, 85]); - Assert.Equal(mergedColor, sourcePixels[65, 137]); - Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]); - - //inside hole - Assert.Equal(Rgba32.Blue, sourcePixels[57, 99]); - - //inside shape - Assert.Equal(Rgba32.Blue, sourcePixels[100, 192]); - } - } - } -} diff --git a/tests/Images/External b/tests/Images/External index 4fe9334aa8..55c250ab80 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 4fe9334aa8898f67b6e3df94bc00867645c3e709 +Subproject commit 55c250ab80f7f9fc26208b9b9d9590cbe8012446 From b90fbc1f6b33e22e40f3cb0f1d2c50fc2bda72ea Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 12 May 2019 13:39:10 +0200 Subject: [PATCH 174/223] move implicit Color conversion to pixel types, add micro-optimizations --- ImageSharp.sln.DotSettings | 1 + src/ImageSharp/Color/Color.Conversions.cs | 133 ++++-------------- src/ImageSharp/Color/Color.cs | 39 ++++- .../PixelImplementations/Argb32.cs | 16 +++ .../PixelImplementations/Bgr24.cs | 16 +++ .../PixelImplementations/Bgra32.cs | 16 +++ .../PixelImplementations/Rgb24.cs | 16 +++ .../PixelImplementations/Rgba32.cs | 16 +++ .../PixelImplementations/Rgba64.cs | 23 +++ tests/ImageSharp.Tests/Color/ColorTests.cs | 2 + 10 files changed, 165 insertions(+), 113 deletions(-) diff --git a/ImageSharp.sln.DotSettings b/ImageSharp.sln.DotSettings index 5268172429..ece3dddb3c 100644 --- a/ImageSharp.sln.DotSettings +++ b/ImageSharp.sln.DotSettings @@ -388,5 +388,6 @@ True True True + True True \ No newline at end of file diff --git a/src/ImageSharp/Color/Color.Conversions.cs b/src/ImageSharp/Color/Color.Conversions.cs index 001aee5a4f..a524b9db1b 100644 --- a/src/ImageSharp/Color/Color.Conversions.cs +++ b/src/ImageSharp/Color/Color.Conversions.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System.Numerics; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; @@ -16,147 +17,67 @@ namespace SixLabors.ImageSharp /// Initializes a new instance of the struct. /// /// The containing the color information. - public Color(Rgba64 pixel) - { - this.data = pixel; - } + [MethodImpl(InliningOptions.ShortMethod)] + public Color(Rgba64 pixel) => this.data = pixel; /// /// Initializes a new instance of the struct. /// /// The containing the color information. - public Color(Rgba32 pixel) - { - this.data = new Rgba64(pixel); - } + [MethodImpl(InliningOptions.ShortMethod)] + public Color(Rgba32 pixel) => this.data = new Rgba64(pixel); /// /// Initializes a new instance of the struct. /// /// The containing the color information. - public Color(Argb32 pixel) - { - this.data = new Rgba64(pixel); - } + [MethodImpl(InliningOptions.ShortMethod)] + public Color(Argb32 pixel) => this.data = new Rgba64(pixel); /// /// Initializes a new instance of the struct. /// /// The containing the color information. - public Color(Bgra32 pixel) - { - this.data = new Rgba64(pixel); - } + [MethodImpl(InliningOptions.ShortMethod)] + public Color(Bgra32 pixel) => this.data = new Rgba64(pixel); /// /// Initializes a new instance of the struct. /// /// The containing the color information. - public Color(Rgb24 pixel) - { - this.data = new Rgba64(pixel); - } + [MethodImpl(InliningOptions.ShortMethod)] + public Color(Rgb24 pixel) => this.data = new Rgba64(pixel); /// /// Initializes a new instance of the struct. /// /// The containing the color information. - public Color(Bgr24 pixel) - { - this.data = new Rgba64(pixel); - } + [MethodImpl(InliningOptions.ShortMethod)] + public Color(Bgr24 pixel) => this.data = new Rgba64(pixel); /// /// Initializes a new instance of the struct. /// /// The containing the color information. - public Color(Vector4 vector) - { - this.data = new Rgba64(vector); - } + [MethodImpl(InliningOptions.ShortMethod)] + public Color(Vector4 vector) => this.data = new Rgba64(vector); - /// - /// Converts an to . - /// - /// The . - /// The . - public static implicit operator Color(Rgba64 source) => new Color(source); - - /// - /// Converts an to . - /// - /// The . - /// The . - public static implicit operator Color(Rgba32 source) => new Color(source); - - /// - /// Converts an to . - /// - /// The . - /// The . - public static implicit operator Color(Bgra32 source) => new Color(source); - - /// - /// Converts an to . - /// - /// The . - /// The . - public static implicit operator Color(Argb32 source) => new Color(source); - - /// - /// Converts an to . - /// - /// The . - /// The . - public static implicit operator Color(Rgb24 source) => new Color(source); - - /// - /// Converts an to . - /// - /// The . - /// The . - public static implicit operator Color(Bgr24 source) => new Color(source); + [MethodImpl(InliningOptions.ShortMethod)] + internal Rgba64 ToRgba64() => this.data; - /// - /// Converts a to . - /// - /// The . - /// The . - public static implicit operator Rgba64(Color color) => color.data; + [MethodImpl(InliningOptions.ShortMethod)] + internal Rgba32 ToRgba32() => this.data.ToRgba32(); - /// - /// Converts a to . - /// - /// The . - /// The . - public static implicit operator Rgba32(Color color) => color.data.ToRgba32(); + [MethodImpl(InliningOptions.ShortMethod)] + internal Bgra32 ToBgra32() => this.data.ToBgra32(); - /// - /// Converts a to . - /// - /// The . - /// The . - public static implicit operator Bgra32(Color color) => color.data.ToBgra32(); + [MethodImpl(InliningOptions.ShortMethod)] + internal Argb32 ToArgb32() => this.data.ToArgb32(); - /// - /// Converts a to . - /// - /// The . - /// The . - public static implicit operator Argb32(Color color) => color.data.ToArgb32(); + [MethodImpl(InliningOptions.ShortMethod)] + internal Rgb24 ToRgb24() => this.data.ToRgb24(); - /// - /// Converts a to . - /// - /// The . - /// The . - public static implicit operator Rgb24(Color color) => color.data.ToRgb24(); - - /// - /// Converts a to . - /// - /// The . - /// The . - public static implicit operator Bgr24(Color color) => color.data.ToBgr24(); + [MethodImpl(InliningOptions.ShortMethod)] + internal Bgr24 ToBgr24() => this.data.ToBgr24(); } } \ No newline at end of file diff --git a/src/ImageSharp/Color/Color.cs b/src/ImageSharp/Color/Color.cs index f2f8578d83..e4640ff632 100644 --- a/src/ImageSharp/Color/Color.cs +++ b/src/ImageSharp/Color/Color.cs @@ -5,6 +5,7 @@ using System; using System.Buffers.Binary; using System.Globalization; using System.Numerics; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.PixelFormats; @@ -24,6 +25,26 @@ namespace SixLabors.ImageSharp { private readonly Rgba64 data; + [MethodImpl(InliningOptions.ShortMethod)] + private Color(byte r, byte g, byte b, byte a) + { + this.data = new Rgba64( + ImageMaths.UpscaleFrom8BitTo16Bit(r), + ImageMaths.UpscaleFrom8BitTo16Bit(g), + ImageMaths.UpscaleFrom8BitTo16Bit(b), + ImageMaths.UpscaleFrom8BitTo16Bit(a)); + } + + [MethodImpl(InliningOptions.ShortMethod)] + private Color(byte r, byte g, byte b) + { + this.data = new Rgba64( + ImageMaths.UpscaleFrom8BitTo16Bit(r), + ImageMaths.UpscaleFrom8BitTo16Bit(g), + ImageMaths.UpscaleFrom8BitTo16Bit(b), + ushort.MaxValue); + } + /// /// Checks whether two structures are equal. /// @@ -33,6 +54,7 @@ namespace SixLabors.ImageSharp /// True if the parameter is equal to the parameter; /// otherwise, false. /// + [MethodImpl(InliningOptions.ShortMethod)] public static bool operator ==(Color left, Color right) { return left.Equals(right); @@ -47,6 +69,7 @@ namespace SixLabors.ImageSharp /// True if the parameter is not equal to the parameter; /// otherwise, false. /// + [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Color left, Color right) { return !left.Equals(right); @@ -60,7 +83,8 @@ namespace SixLabors.ImageSharp /// The blue component (0-255). /// The alpha component (0-255). /// The . - public static Color FromRgba(byte r, byte g, byte b, byte a) => new Color(new Rgba32(r, g, b, a)); + [MethodImpl(InliningOptions.ShortMethod)] + public static Color FromRgba(byte r, byte g, byte b, byte a) => new Color(r, g, b, a); /// /// Creates a from RGB bytes. @@ -69,7 +93,8 @@ namespace SixLabors.ImageSharp /// The green component (0-255). /// The blue component (0-255). /// The . - public static Color FromRgb(byte r, byte g, byte b) => FromRgba(r, g, b, 255); + [MethodImpl(InliningOptions.ShortMethod)] + public static Color FromRgb(byte r, byte g, byte b) => new Color(r, g, b); /// /// Creates a new instance from the string representing a color in hexadecimal form. @@ -98,6 +123,7 @@ namespace SixLabors.ImageSharp /// Gets the hexadecimal representation of the color instance in rrggbbaa form. /// /// A hexadecimal string representation of the value. + [MethodImpl(InliningOptions.ShortMethod)] public string ToHex() => this.data.ToRgba32().ToHex(); /// @@ -109,6 +135,7 @@ namespace SixLabors.ImageSharp /// /// The pixel type to convert to. /// The pixel value. + [MethodImpl(InliningOptions.ShortMethod)] public TPixel ToPixel() where TPixel : struct, IPixel { @@ -118,6 +145,7 @@ namespace SixLabors.ImageSharp } /// + [MethodImpl(InliningOptions.ShortMethod)] public bool Equals(Color other) { return this.data.PackedValue == other.data.PackedValue; @@ -126,15 +154,11 @@ namespace SixLabors.ImageSharp /// public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) - { - return false; - } - return obj is Color other && this.Equals(other); } /// + [MethodImpl(InliningOptions.ShortMethod)] public override int GetHashCode() { return this.data.PackedValue.GetHashCode(); @@ -143,6 +167,7 @@ namespace SixLabors.ImageSharp /// /// Bulk convert a span of to a span of a specified pixel type. /// + [MethodImpl(InliningOptions.ShortMethod)] internal static void ToPixel( Configuration configuration, ReadOnlySpan source, diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs index 075df01cd2..8981c87458 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs @@ -142,6 +142,22 @@ namespace SixLabors.ImageSharp.PixelFormats set => this.Argb = value; } + /// + /// Converts an to . + /// + /// The . + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public static implicit operator Color(Argb32 source) => new Color(source); + + /// + /// Converts a to . + /// + /// The . + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public static implicit operator Argb32(Color color) => color.ToArgb32(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs index 3ba6436a0b..a0b059dfce 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs @@ -50,6 +50,22 @@ namespace SixLabors.ImageSharp.PixelFormats this.B = b; } + /// + /// Converts an to . + /// + /// The . + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public static implicit operator Color(Bgr24 source) => new Color(source); + + /// + /// Converts a to . + /// + /// The . + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public static implicit operator Bgr24(Color color) => color.ToBgr24(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs index 758be8043c..ea7a961885 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs @@ -98,6 +98,22 @@ namespace SixLabors.ImageSharp.PixelFormats set => this.Bgra = value; } + /// + /// Converts an to . + /// + /// The . + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public static implicit operator Color(Bgra32 source) => new Color(source); + + /// + /// Converts a to . + /// + /// The . + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public static implicit operator Bgra32(Color color) => color.ToBgra32(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs index 1255f66d15..469dbbad4a 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs @@ -53,6 +53,22 @@ namespace SixLabors.ImageSharp.PixelFormats this.B = b; } + /// + /// Converts an to . + /// + /// The . + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public static implicit operator Color(Rgb24 source) => new Color(source); + + /// + /// Converts a to . + /// + /// The . + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public static implicit operator Rgb24(Color color) => color.ToRgb24(); + /// /// Allows the implicit conversion of an instance of to a /// . diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs index 7367c44634..c58d17ef42 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs @@ -169,6 +169,22 @@ namespace SixLabors.ImageSharp.PixelFormats set => this.Rgba = value; } + /// + /// Converts an to . + /// + /// The . + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public static implicit operator Color(Rgba32 source) => new Color(source); + + /// + /// Converts a to . + /// + /// The . + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public static implicit operator Rgba32(Color color) => color.ToRgba32(); + /// /// Allows the implicit conversion of an instance of to a /// . diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs index cf9d4a5527..978d9b0156 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs @@ -46,6 +46,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// The green component. /// The blue component. /// The alpha component. + [MethodImpl(InliningOptions.ShortMethod)] public Rgba64(ushort r, ushort g, ushort b, ushort a) { this.R = r; @@ -58,6 +59,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// Initializes a new instance of the struct. /// /// A structure of 4 bytes in RGBA byte order. + [MethodImpl(InliningOptions.ShortMethod)] public Rgba64(Rgba32 source) { this.R = ImageMaths.UpscaleFrom8BitTo16Bit(source.R); @@ -70,6 +72,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// Initializes a new instance of the struct. /// /// A structure of 4 bytes in BGRA byte order. + [MethodImpl(InliningOptions.ShortMethod)] public Rgba64(Bgra32 source) { this.R = ImageMaths.UpscaleFrom8BitTo16Bit(source.R); @@ -82,6 +85,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// Initializes a new instance of the struct. /// /// A structure of 4 bytes in ARGB byte order. + [MethodImpl(InliningOptions.ShortMethod)] public Rgba64(Argb32 source) { this.R = ImageMaths.UpscaleFrom8BitTo16Bit(source.R); @@ -94,6 +98,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// Initializes a new instance of the struct. /// /// A structure of 3 bytes in RGB byte order. + [MethodImpl(InliningOptions.ShortMethod)] public Rgba64(Rgb24 source) { this.R = ImageMaths.UpscaleFrom8BitTo16Bit(source.R); @@ -106,6 +111,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// Initializes a new instance of the struct. /// /// A structure of 3 bytes in BGR byte order. + [MethodImpl(InliningOptions.ShortMethod)] public Rgba64(Bgr24 source) { this.R = ImageMaths.UpscaleFrom8BitTo16Bit(source.R); @@ -118,6 +124,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// Initializes a new instance of the struct. /// /// The . + [MethodImpl(InliningOptions.ShortMethod)] public Rgba64(Vector4 vector) { vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One) * Max; @@ -149,6 +156,22 @@ namespace SixLabors.ImageSharp.PixelFormats set => Unsafe.As(ref this) = value; } + /// + /// Converts an to . + /// + /// The . + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public static implicit operator Color(Rgba64 source) => new Color(source); + + /// + /// Converts a to . + /// + /// The . + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public static implicit operator Rgba64(Color color) => color.ToPixel(); + /// /// Compares two objects for equality. /// diff --git a/tests/ImageSharp.Tests/Color/ColorTests.cs b/tests/ImageSharp.Tests/Color/ColorTests.cs index 729ef94e3c..b2559d1d16 100644 --- a/tests/ImageSharp.Tests/Color/ColorTests.cs +++ b/tests/ImageSharp.Tests/Color/ColorTests.cs @@ -38,6 +38,8 @@ namespace SixLabors.ImageSharp.Tests Assert.False(c1 == c2); Assert.True(c1 != c2); + + Assert.False(c1.Equals(null)); } [Fact] From 0cbb800eac54abbed6b40c3bc9c75c21d69f304c Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 12 May 2019 13:44:53 +0200 Subject: [PATCH 175/223] fix merge issues --- src/ImageSharp/Color/Color.Conversions.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ImageSharp/Color/Color.Conversions.cs b/src/ImageSharp/Color/Color.Conversions.cs index 3123d89e96..16d673d85d 100644 --- a/src/ImageSharp/Color/Color.Conversions.cs +++ b/src/ImageSharp/Color/Color.Conversions.cs @@ -62,12 +62,19 @@ namespace SixLabors.ImageSharp [MethodImpl(InliningOptions.ShortMethod)] public Color(Vector4 vector) => this.data = new Rgba64(vector); - [MethodImpl(InliningOptions.ShortMethod)] + /// + /// Converts a to . + /// + /// The . + /// The . + public static explicit operator Vector4(Color color) => color.data.ToVector4(); + /// /// Converts an to . /// /// The . /// The . + [MethodImpl(InliningOptions.ShortMethod)] public static explicit operator Color(Vector4 source) => new Color(source); [MethodImpl(InliningOptions.ShortMethod)] @@ -84,12 +91,5 @@ namespace SixLabors.ImageSharp [MethodImpl(InliningOptions.ShortMethod)] internal Bgr24 ToBgr24() => this.data.ToBgr24(); - - /// - /// Converts a to . - /// - /// The . - /// The . - public static explicit operator Vector4(Color color) => color.data.ToVector4(); } } \ No newline at end of file From 253e8d3a0e77d193a2b543b452579a89571bb7f8 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 12 May 2019 14:37:02 +0200 Subject: [PATCH 176/223] DrawImageTests: add tolerance to make all test configurations happy --- tests/ImageSharp.Tests/Drawing/DrawImageTests.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs b/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs index f4e9b7b450..70879e6feb 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs @@ -91,7 +91,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing } image.DebugSave(provider, testInfo, encoder: encoder); - image.CompareToReferenceOutput(provider, testInfo); + image.CompareToReferenceOutput(ImageComparer.TolerantPercentage(0.01f), + provider, + testInfo); } } @@ -146,7 +148,10 @@ namespace SixLabors.ImageSharp.Tests.Drawing image.Mutate(x => x.DrawImage(blend, position, .75F)); image.DebugSave(provider, appendSourceFileOrDescription: false, appendPixelTypeToFileName: false); - image.CompareToReferenceOutput(provider, appendSourceFileOrDescription: false, appendPixelTypeToFileName: false); + image.CompareToReferenceOutput(ImageComparer.TolerantPercentage(0.002f), + provider, + appendSourceFileOrDescription: false, + appendPixelTypeToFileName: false); } } From 7b81ea5e6bab2084d2f26da86dfa4052b3cab9dd Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 12 May 2019 18:38:22 +0200 Subject: [PATCH 177/223] Review changes --- src/ImageSharp/Formats/Bmp/BmpEncoder.cs | 7 +++ src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs | 61 ++++++++++--------- .../Formats/Bmp/IBmpEncoderOptions.cs | 9 ++- 3 files changed, 47 insertions(+), 30 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoder.cs b/src/ImageSharp/Formats/Bmp/BmpEncoder.cs index f3eb9282ce..612675c33c 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoder.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoder.cs @@ -4,6 +4,7 @@ using System.IO; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors.Quantization; namespace SixLabors.ImageSharp.Formats.Bmp { @@ -25,6 +26,12 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// public bool SupportTransparency { get; set; } + /// + /// Gets or sets the quantizer for reducing the color count for 8-Bit images. + /// Defaults to OctreeQuantizer. + /// + public IQuantizer Quantizer { get; set; } + /// public void Encode(Image image, Stream stream) where TPixel : struct, IPixel diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index 8d0e8578d1..90ea673d3e 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.IO; using SixLabors.ImageSharp.Advanced; @@ -62,6 +63,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// private readonly bool writeV4Header; + /// + /// The quantizer for reducing the color count for 8-Bit images. + /// + private readonly IQuantizer quantizer; + /// /// Initializes a new instance of the class. /// @@ -72,6 +78,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp this.memoryAllocator = memoryAllocator; this.bitsPerPixel = options.BitsPerPixel; this.writeV4Header = options.SupportTransparency; + this.quantizer = options.Quantizer ?? new OctreeQuantizer(dither: true, maxColors: 256); } /// @@ -298,39 +305,35 @@ namespace SixLabors.ImageSharp.Formats.Bmp private void Write8Bit(Stream stream, ImageFrame image) where TPixel : struct, IPixel { -#if NETCOREAPP2_1 - Span colorPalette = stackalloc byte[ColorPaletteSize8Bit]; -#else - byte[] colorPalette = new byte[ColorPaletteSize8Bit]; -#endif - - var quantizer = new OctreeQuantizer(dither: true, maxColors: 256); - QuantizedFrame quantized = quantizer.CreateFrameQuantizer(this.configuration).QuantizeFrame(image); - - int idx = 0; - var color = default(Rgba32); - foreach (TPixel quantizedColor in quantized.Palette) + using (IMemoryOwner colorPaletteBuffer = this.memoryAllocator.AllocateManagedByteBuffer(ColorPaletteSize8Bit, AllocationOptions.Clean)) + using (QuantizedFrame quantized = this.quantizer.CreateFrameQuantizer(this.configuration, 256).QuantizeFrame(image)) { - quantizedColor.ToRgba32(ref color); - colorPalette[idx] = color.B; - colorPalette[idx + 1] = color.G; - colorPalette[idx + 2] = color.R; - - // Padding byte, always 0 - colorPalette[idx + 3] = 0; - idx += 4; - } - - stream.Write(colorPalette, 0, ColorPaletteSize8Bit); + Span colorPalette = colorPaletteBuffer.GetSpan(); + int idx = 0; + var color = default(Rgba32); + foreach (TPixel quantizedColor in quantized.Palette) + { + quantizedColor.ToRgba32(ref color); + colorPalette[idx] = color.B; + colorPalette[idx + 1] = color.G; + colorPalette[idx + 2] = color.R; + + // Padding byte, always 0 + colorPalette[idx + 3] = 0; + idx += 4; + } - for (int y = image.Height - 1; y >= 0; y--) - { - Span pixelSpan = quantized.GetRowSpan(y); - stream.Write(pixelSpan); + stream.Write(colorPalette); - for (int i = 0; i < this.padding; i++) + for (int y = image.Height - 1; y >= 0; y--) { - stream.WriteByte(0); + Span pixelSpan = quantized.GetRowSpan(y); + stream.Write(pixelSpan); + + for (int i = 0; i < this.padding; i++) + { + stream.WriteByte(0); + } } } } diff --git a/src/ImageSharp/Formats/Bmp/IBmpEncoderOptions.cs b/src/ImageSharp/Formats/Bmp/IBmpEncoderOptions.cs index 92ff092705..59ad929df7 100644 --- a/src/ImageSharp/Formats/Bmp/IBmpEncoderOptions.cs +++ b/src/ImageSharp/Formats/Bmp/IBmpEncoderOptions.cs @@ -1,10 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.Processing.Processors.Quantization; + namespace SixLabors.ImageSharp.Formats.Bmp { /// - /// Configuration options for use during bmp encoding + /// Configuration options for use during bmp encoding. /// internal interface IBmpEncoderOptions { @@ -20,5 +22,10 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// Instead a bitmap version 4 info header will be written with the BITFIELDS compression. /// bool SupportTransparency { get; } + + /// + /// Gets the quantizer for reducing the color count for 8-Bit images. + /// + IQuantizer Quantizer { get; } } } \ No newline at end of file From 90c0772d96e988114e61a7da06bf93f300b98416 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 12 May 2019 20:39:01 +0200 Subject: [PATCH 178/223] started the refactor --- .../Processing/DrawingHelpers.cs | 19 ++++++++++++ src/ImageSharp.Drawing/Processing/IBrush.cs | 9 ++++-- src/ImageSharp.Drawing/Processing/IPen.cs | 16 +++------- .../Processing/PatternBrush{TPixel}.cs | 30 +++++++++++-------- .../Processing/SolidBrush{TPixel}.cs | 18 +++++------ src/ImageSharp/Color/Color.Conversions.cs | 3 ++ 6 files changed, 59 insertions(+), 36 deletions(-) create mode 100644 src/ImageSharp.Drawing/Processing/DrawingHelpers.cs diff --git a/src/ImageSharp.Drawing/Processing/DrawingHelpers.cs b/src/ImageSharp.Drawing/Processing/DrawingHelpers.cs new file mode 100644 index 0000000000..314b65f7e7 --- /dev/null +++ b/src/ImageSharp.Drawing/Processing/DrawingHelpers.cs @@ -0,0 +1,19 @@ +// // Copyright (c) Six Labors and contributors. +// // Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Primitives; + +namespace SixLabors.ImageSharp.Processing +{ + internal static class DrawingHelpers + { + public static DenseMatrix ToPixelMatrix(this DenseMatrix colorMatrix, Configuration configuration) + where TPixel : struct, IPixel + { + DenseMatrix result = new DenseMatrix(colorMatrix.Columns, colorMatrix.Rows); + Color.ToPixel(configuration, colorMatrix.Span, result.Span); + return result; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/IBrush.cs b/src/ImageSharp.Drawing/Processing/IBrush.cs index a3c94a1b5a..aa8c25b950 100644 --- a/src/ImageSharp.Drawing/Processing/IBrush.cs +++ b/src/ImageSharp.Drawing/Processing/IBrush.cs @@ -14,8 +14,7 @@ namespace SixLabors.ImageSharp.Processing /// A brush is a simple class that will return an that will perform the /// logic for converting a pixel location to a . /// - public interface IBrush - where TPixel : struct, IPixel + public interface IBrush { /// /// Creates the applicator for this brush. @@ -30,6 +29,10 @@ namespace SixLabors.ImageSharp.Processing /// The when being applied to things like shapes would usually be the /// bounding box of the shape not necessarily the bounds of the whole image /// - BrushApplicator CreateApplicator(ImageFrame source, RectangleF region, GraphicsOptions options); + BrushApplicator CreateApplicator( + ImageFrame source, + RectangleF region, + GraphicsOptions options) + where TPixel : struct, IPixel; } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/IPen.cs b/src/ImageSharp.Drawing/Processing/IPen.cs index 6f63dcfd0f..b9e359d601 100644 --- a/src/ImageSharp.Drawing/Processing/IPen.cs +++ b/src/ImageSharp.Drawing/Processing/IPen.cs @@ -7,23 +7,15 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Processing { /// - /// Interface representing a Pen + /// Interface representing the pattern and size of the stroke to apply with a Pen. /// - /// The type of the color. - public interface IPen : IPen - where TPixel : struct, IPixel + public interface IPen { /// /// Gets the stroke fill. /// - IBrush StrokeFill { get; } - } - - /// - /// Interface representing the pattern and size of the stroke to apply with a Pen. - /// - public interface IPen - { + IBrush StrokeFill { get; } + /// /// Gets the width to apply to the stroke /// diff --git a/src/ImageSharp.Drawing/Processing/PatternBrush{TPixel}.cs b/src/ImageSharp.Drawing/Processing/PatternBrush{TPixel}.cs index 20161b517d..249383f3b1 100644 --- a/src/ImageSharp.Drawing/Processing/PatternBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/PatternBrush{TPixel}.cs @@ -35,13 +35,12 @@ namespace SixLabors.ImageSharp.Processing /// /// /// The pixel format. - public class PatternBrush : IBrush - where TPixel : struct, IPixel + public class PatternBrush : IBrush { /// /// The pattern. /// - private readonly DenseMatrix pattern; + private readonly DenseMatrix pattern; private readonly DenseMatrix patternVector; /// @@ -50,7 +49,7 @@ namespace SixLabors.ImageSharp.Processing /// Color of the fore. /// Color of the back. /// The pattern. - public PatternBrush(TPixel foreColor, TPixel backColor, bool[,] pattern) + public PatternBrush(Color foreColor, Color backColor, bool[,] pattern) : this(foreColor, backColor, new DenseMatrix(pattern)) { } @@ -61,11 +60,11 @@ namespace SixLabors.ImageSharp.Processing /// Color of the fore. /// Color of the back. /// The pattern. - internal PatternBrush(TPixel foreColor, TPixel backColor, in DenseMatrix pattern) + internal PatternBrush(Color foreColor, Color backColor, in DenseMatrix pattern) { var foreColorVector = foreColor.ToVector4(); var backColorVector = backColor.ToVector4(); - this.pattern = new DenseMatrix(pattern.Columns, pattern.Rows); + this.pattern = new DenseMatrix(pattern.Columns, pattern.Rows); this.patternVector = new DenseMatrix(pattern.Columns, pattern.Rows); for (int i = 0; i < pattern.Data.Length; i++) { @@ -86,19 +85,25 @@ namespace SixLabors.ImageSharp.Processing /// Initializes a new instance of the class. /// /// The brush. - internal PatternBrush(PatternBrush brush) + internal PatternBrush(PatternBrush brush) { this.pattern = brush.pattern; this.patternVector = brush.patternVector; } /// - public BrushApplicator CreateApplicator(ImageFrame source, RectangleF region, GraphicsOptions options) => new PatternBrushApplicator(source, this.pattern, this.patternVector, options); + public BrushApplicator CreateApplicator( + ImageFrame source, + RectangleF region, + GraphicsOptions options) + where TPixel : struct, IPixel => + new PatternBrushApplicator(source, this.pattern, this.patternVector, options); /// /// The pattern brush applicator. /// - private class PatternBrushApplicator : BrushApplicator + private class PatternBrushApplicator : BrushApplicator + where TPixel : struct, IPixel { /// /// The pattern. @@ -107,16 +112,17 @@ namespace SixLabors.ImageSharp.Processing private readonly DenseMatrix patternVector; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The source image. /// The pattern. /// The patternVector. /// The options - public PatternBrushApplicator(ImageFrame source, in DenseMatrix pattern, DenseMatrix patternVector, GraphicsOptions options) + public PatternBrushApplicator(ImageFrame source, in DenseMatrix pattern, DenseMatrix patternVector, GraphicsOptions options) : base(source, options) { - this.pattern = pattern; + this.pattern = new DenseMatrix(pattern.Columns, pattern.Rows); + Color.ToPixel(source.Configuration, pattern.Data, this.pattern.Data); this.patternVector = patternVector; } diff --git a/src/ImageSharp.Drawing/Processing/SolidBrush{TPixel}.cs b/src/ImageSharp.Drawing/Processing/SolidBrush{TPixel}.cs index 20a6833c40..75ca5aa7e8 100644 --- a/src/ImageSharp.Drawing/Processing/SolidBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/SolidBrush{TPixel}.cs @@ -15,20 +15,18 @@ namespace SixLabors.ImageSharp.Processing /// /// Provides an implementation of a solid brush for painting solid color areas. /// - /// The pixel format. - public class SolidBrush : IBrush - where TPixel : struct, IPixel + public class SolidBrush : IBrush { /// /// The color to paint. /// - private readonly TPixel color; + private readonly Color color; /// /// Initializes a new instance of the class. /// /// The color. - public SolidBrush(TPixel color) + public SolidBrush(Color color) { this.color = color; } @@ -39,18 +37,20 @@ namespace SixLabors.ImageSharp.Processing /// /// The color. /// - public TPixel Color => this.color; + public Color Color => this.color; /// - public BrushApplicator CreateApplicator(ImageFrame source, RectangleF region, GraphicsOptions options) + public BrushApplicator CreateApplicator(ImageFrame source, RectangleF region, GraphicsOptions options) + where TPixel : struct, IPixel { - return new SolidBrushApplicator(source, this.color, options); + return new SolidBrushApplicator(source, this.color.ToPixel(), options); } /// /// The solid brush applicator. /// - private class SolidBrushApplicator : BrushApplicator + private class SolidBrushApplicator : BrushApplicator + where TPixel : struct, IPixel { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Color/Color.Conversions.cs b/src/ImageSharp/Color/Color.Conversions.cs index 16d673d85d..a6e0717f48 100644 --- a/src/ImageSharp/Color/Color.Conversions.cs +++ b/src/ImageSharp/Color/Color.Conversions.cs @@ -91,5 +91,8 @@ namespace SixLabors.ImageSharp [MethodImpl(InliningOptions.ShortMethod)] internal Bgr24 ToBgr24() => this.data.ToBgr24(); + + [MethodImpl(InliningOptions.ShortMethod)] + internal Vector4 ToVector4() => this.data.ToVector4(); } } \ No newline at end of file From 0e2c53e5ed2e25b5c2f3513ec45958df2e1620da Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 12 May 2019 21:08:19 +0200 Subject: [PATCH 179/223] Pen, Brush & Processors refactored --- src/ImageSharp.Drawing/Processing/Brushes.cs | 120 ++++----- .../Processing/DrawBezierExtensions.cs | 44 ++-- .../Processing/ImageBrush{TPixel}.cs | 28 +-- .../Processing/PatternBrush{TPixel}.cs | 11 +- src/ImageSharp.Drawing/Processing/Pens.cs | 50 +--- .../Processing/Pen{TPixel}.cs | 18 +- .../Processors/Drawing/FillProcessor.cs | 57 +++-- .../Processors/Drawing/FillRegionProcessor.cs | 63 +++-- .../Processors/Text/DrawTextProcessor.cs | 55 ++++- .../Processing/RecolorBrush{TPixel}.cs | 29 ++- src/ImageSharp/GraphicsOptions.cs | 231 +++++++++--------- 11 files changed, 356 insertions(+), 350 deletions(-) diff --git a/src/ImageSharp.Drawing/Processing/Brushes.cs b/src/ImageSharp.Drawing/Processing/Brushes.cs index c5e7a3e9ff..bd73e1f1b6 100644 --- a/src/ImageSharp.Drawing/Processing/Brushes.cs +++ b/src/ImageSharp.Drawing/Processing/Brushes.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing /// /// A collection of methods for creating generic brushes. /// - /// A New + /// A New public static class Brushes { /// @@ -94,163 +94,131 @@ namespace SixLabors.ImageSharp.Processing /// Create as brush that will paint a solid color /// /// The color. - /// The pixel format. - /// A New - public static SolidBrush Solid(TPixel color) - where TPixel : struct, IPixel - => new SolidBrush(color); + /// A New + public static SolidBrush Solid(Color color) => new SolidBrush(color); /// /// Create as brush that will paint a Percent10 Hatch Pattern with the specified colors /// /// Color of the foreground. - /// The pixel format. - /// A New - public static PatternBrush Percent10(TPixel foreColor) - where TPixel : struct, IPixel - => new PatternBrush(foreColor, NamedColors.Transparent, Percent10Pattern); + /// A New + public static PatternBrush Percent10(Color foreColor) => + new PatternBrush(foreColor, Color.Transparent, Percent10Pattern); /// /// Create as brush that will paint a Percent10 Hatch Pattern with the specified colors /// /// Color of the foreground. /// Color of the background. - /// The pixel format. - /// A New - public static PatternBrush Percent10(TPixel foreColor, TPixel backColor) - where TPixel : struct, IPixel - => new PatternBrush(foreColor, backColor, Percent10Pattern); + /// A New + public static PatternBrush Percent10(Color foreColor, Color backColor) => + new PatternBrush(foreColor, backColor, Percent10Pattern); /// /// Create as brush that will paint a Percent20 Hatch Pattern with the specified foreground color and a /// transparent background. /// /// Color of the foreground. - /// The pixel format. - /// A New - public static PatternBrush Percent20(TPixel foreColor) - where TPixel : struct, IPixel - => new PatternBrush(foreColor, NamedColors.Transparent, Percent20Pattern); + /// A New + public static PatternBrush Percent20(Color foreColor) => + new PatternBrush(foreColor, Color.Transparent, Percent20Pattern); /// /// Create as brush that will paint a Percent20 Hatch Pattern with the specified colors /// /// Color of the foreground. /// Color of the background. - /// The pixel format. - /// A New - public static PatternBrush Percent20(TPixel foreColor, TPixel backColor) - where TPixel : struct, IPixel - => new PatternBrush(foreColor, backColor, Percent20Pattern); + /// A New + public static PatternBrush Percent20(Color foreColor, Color backColor) => + new PatternBrush(foreColor, backColor, Percent20Pattern); /// /// Create as brush that will paint a Horizontal Hatch Pattern with the specified foreground color and a /// transparent background. /// /// Color of the foreground. - /// The pixel format. - /// A New - public static PatternBrush Horizontal(TPixel foreColor) - where TPixel : struct, IPixel - => new PatternBrush(foreColor, NamedColors.Transparent, HorizontalPattern); + /// A New + public static PatternBrush Horizontal(Color foreColor) => + new PatternBrush(foreColor, Color.Transparent, HorizontalPattern); /// /// Create as brush that will paint a Horizontal Hatch Pattern with the specified colors /// /// Color of the foreground. /// Color of the background. - /// The pixel format. - /// A New - public static PatternBrush Horizontal(TPixel foreColor, TPixel backColor) - where TPixel : struct, IPixel - => new PatternBrush(foreColor, backColor, HorizontalPattern); + /// A New + public static PatternBrush Horizontal(Color foreColor, Color backColor) => + new PatternBrush(foreColor, backColor, HorizontalPattern); /// /// Create as brush that will paint a Min Hatch Pattern with the specified foreground color and a /// transparent background. /// /// Color of the foreground. - /// The pixel format. - /// A New - public static PatternBrush Min(TPixel foreColor) - where TPixel : struct, IPixel - => new PatternBrush(foreColor, NamedColors.Transparent, MinPattern); + /// A New + public static PatternBrush Min(Color foreColor) => new PatternBrush(foreColor, Color.Transparent, MinPattern); /// /// Create as brush that will paint a Min Hatch Pattern with the specified colors /// /// Color of the foreground. /// Color of the background. - /// The pixel format. - /// A New - public static PatternBrush Min(TPixel foreColor, TPixel backColor) - where TPixel : struct, IPixel - => new PatternBrush(foreColor, backColor, MinPattern); + /// A New + public static PatternBrush Min(Color foreColor, Color backColor) => + new PatternBrush(foreColor, backColor, MinPattern); /// /// Create as brush that will paint a Vertical Hatch Pattern with the specified foreground color and a /// transparent background. /// /// Color of the foreground. - /// The pixel format. - /// A New - public static PatternBrush Vertical(TPixel foreColor) - where TPixel : struct, IPixel - => new PatternBrush(foreColor, NamedColors.Transparent, VerticalPattern); + /// A New + public static PatternBrush Vertical(Color foreColor) => + new PatternBrush(foreColor, Color.Transparent, VerticalPattern); /// /// Create as brush that will paint a Vertical Hatch Pattern with the specified colors /// /// Color of the foreground. /// Color of the background. - /// The pixel format. - /// A New - public static PatternBrush Vertical(TPixel foreColor, TPixel backColor) - where TPixel : struct, IPixel - => new PatternBrush(foreColor, backColor, VerticalPattern); + /// A New + public static PatternBrush Vertical(Color foreColor, Color backColor) => + new PatternBrush(foreColor, backColor, VerticalPattern); /// /// Create as brush that will paint a Forward Diagonal Hatch Pattern with the specified foreground color and a /// transparent background. /// /// Color of the foreground. - /// The pixel format. - /// A New - public static PatternBrush ForwardDiagonal(TPixel foreColor) - where TPixel : struct, IPixel - => new PatternBrush(foreColor, NamedColors.Transparent, ForwardDiagonalPattern); + /// A New + public static PatternBrush ForwardDiagonal(Color foreColor) => + new PatternBrush(foreColor, Color.Transparent, ForwardDiagonalPattern); /// /// Create as brush that will paint a Forward Diagonal Hatch Pattern with the specified colors /// /// Color of the foreground. /// Color of the background. - /// The pixel format. - /// A New - public static PatternBrush ForwardDiagonal(TPixel foreColor, TPixel backColor) - where TPixel : struct, IPixel - => new PatternBrush(foreColor, backColor, ForwardDiagonalPattern); + /// A New + public static PatternBrush ForwardDiagonal(Color foreColor, Color backColor) => + new PatternBrush(foreColor, backColor, ForwardDiagonalPattern); /// /// Create as brush that will paint a Backward Diagonal Hatch Pattern with the specified foreground color and a /// transparent background. /// /// Color of the foreground. - /// The pixel format. - /// A New - public static PatternBrush BackwardDiagonal(TPixel foreColor) - where TPixel : struct, IPixel - => new PatternBrush(foreColor, NamedColors.Transparent, BackwardDiagonalPattern); + /// A New + public static PatternBrush BackwardDiagonal(Color foreColor) => + new PatternBrush(foreColor, Color.Transparent, BackwardDiagonalPattern); /// /// Create as brush that will paint a Backward Diagonal Hatch Pattern with the specified colors /// /// Color of the foreground. /// Color of the background. - /// The pixel format. - /// A New - public static PatternBrush BackwardDiagonal(TPixel foreColor, TPixel backColor) - where TPixel : struct, IPixel - => new PatternBrush(foreColor, backColor, BackwardDiagonalPattern); + /// A New + public static PatternBrush BackwardDiagonal(Color foreColor, Color backColor) => + new PatternBrush(foreColor, backColor, BackwardDiagonalPattern); } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/DrawBezierExtensions.cs b/src/ImageSharp.Drawing/Processing/DrawBezierExtensions.cs index 782f5d4d73..991c01293a 100644 --- a/src/ImageSharp.Drawing/Processing/DrawBezierExtensions.cs +++ b/src/ImageSharp.Drawing/Processing/DrawBezierExtensions.cs @@ -15,80 +15,80 @@ namespace SixLabors.ImageSharp.Processing /// /// Draws the provided points as an open Bezier path at the provided thickness with the supplied brush /// - /// The type of the color. + /// The image this method extends. /// The options. /// The brush. /// The thickness. /// The points. /// The . - public static IImageProcessingContext DrawBeziers(this IImageProcessingContext source, GraphicsOptions options, IBrush brush, float thickness, params PointF[] points) - where TPixel : struct, IPixel - => source.Draw(options, new Pen(brush, thickness), new Path(new CubicBezierLineSegment(points))); + public static IImageProcessingContext DrawBeziers(this IImageProcessingContext source, GraphicsOptions options, IBrush brush, float thickness, params PointF[] points) + + => source.Draw(options, new Pen(brush, thickness), new Path(new CubicBezierLineSegment(points))); /// /// Draws the provided points as an open Bezier path at the provided thickness with the supplied brush /// - /// The type of the color. + /// The image this method extends. /// The brush. /// The thickness. /// The points. /// The . - public static IImageProcessingContext DrawBeziers(this IImageProcessingContext source, IBrush brush, float thickness, params PointF[] points) - where TPixel : struct, IPixel - => source.Draw(new Pen(brush, thickness), new Path(new CubicBezierLineSegment(points))); + public static IImageProcessingContext DrawBeziers(this IImageProcessingContext source, IBrush brush, float thickness, params PointF[] points) + + => source.Draw(new Pen(brush, thickness), new Path(new CubicBezierLineSegment(points))); /// /// Draws the provided points as an open Bezier path at the provided thickness with the supplied brush /// - /// The type of the color. + /// The image this method extends. /// The color. /// The thickness. /// The points. /// The . - public static IImageProcessingContext DrawBeziers(this IImageProcessingContext source, TPixel color, float thickness, params PointF[] points) - where TPixel : struct, IPixel - => source.DrawBeziers(new SolidBrush(color), thickness, points); + public static IImageProcessingContext DrawBeziers(this IImageProcessingContext source, TPixel color, float thickness, params PointF[] points) + + => source.DrawBeziers(new SolidBrush(color), thickness, points); /// /// Draws the provided points as an open Bezier path at the provided thickness with the supplied brush /// - /// The type of the color. + /// The image this method extends. /// The options. /// The color. /// The thickness. /// The points. /// The . - public static IImageProcessingContext DrawBeziers(this IImageProcessingContext source, GraphicsOptions options, TPixel color, float thickness, params PointF[] points) - where TPixel : struct, IPixel - => source.DrawBeziers(options, new SolidBrush(color), thickness, points); + public static IImageProcessingContext DrawBeziers(this IImageProcessingContext source, GraphicsOptions options, TPixel color, float thickness, params PointF[] points) + + => source.DrawBeziers(options, new SolidBrush(color), thickness, points); /// /// Draws the provided points as an open Bezier path with the supplied pen /// - /// The type of the color. + /// The image this method extends. /// The options. /// The pen. /// The points. /// The . - public static IImageProcessingContext DrawBeziers(this IImageProcessingContext source, GraphicsOptions options, IPen pen, params PointF[] points) - where TPixel : struct, IPixel + public static IImageProcessingContext DrawBeziers(this IImageProcessingContext source, GraphicsOptions options, IPen pen, params PointF[] points) + => source.Draw(options, pen, new Path(new CubicBezierLineSegment(points))); /// /// Draws the provided points as an open Bezier path with the supplied pen /// - /// The type of the color. + /// The image this method extends. /// The pen. /// The points. /// The . - public static IImageProcessingContext DrawBeziers(this IImageProcessingContext source, IPen pen, params PointF[] points) - where TPixel : struct, IPixel + public static IImageProcessingContext DrawBeziers(this IImageProcessingContext source, IPen pen, params PointF[] points) + => source.Draw(pen, new Path(new CubicBezierLineSegment(points))); } } diff --git a/src/ImageSharp.Drawing/Processing/ImageBrush{TPixel}.cs b/src/ImageSharp.Drawing/Processing/ImageBrush{TPixel}.cs index cfb3b2ea4f..d5ec1123d7 100644 --- a/src/ImageSharp.Drawing/Processing/ImageBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/ImageBrush{TPixel}.cs @@ -15,40 +15,38 @@ namespace SixLabors.ImageSharp.Processing /// Provides an implementation of an image brush for painting images within areas. /// /// The pixel format. - public class ImageBrush : IBrush - where TPixel : struct, IPixel + public class ImageBrush : IBrush { /// /// The image to paint. /// - private readonly ImageFrame image; + private readonly Image image; /// /// Initializes a new instance of the class. /// /// The image. - public ImageBrush(ImageFrame image) + public ImageBrush(Image image) { this.image = image; } - /// - /// Initializes a new instance of the class. - /// - /// The image. - public ImageBrush(Image image) - : this(image.Frames.RootFrame) + /// + public BrushApplicator CreateApplicator( + ImageFrame source, + RectangleF region, + GraphicsOptions options) + where TPixel : struct, IPixel { + Image specificImage = (Image)this.image; + return new ImageBrushApplicator(source, specificImage.Frames.RootFrame, region, options); } - /// - public BrushApplicator CreateApplicator(ImageFrame source, RectangleF region, GraphicsOptions options) - => new ImageBrushApplicator(source, this.image, region, options); - /// /// The image brush applicator. /// - private class ImageBrushApplicator : BrushApplicator + private class ImageBrushApplicator : BrushApplicator + where TPixel : struct, IPixel { /// /// The source image. diff --git a/src/ImageSharp.Drawing/Processing/PatternBrush{TPixel}.cs b/src/ImageSharp.Drawing/Processing/PatternBrush{TPixel}.cs index 249383f3b1..0e66756721 100644 --- a/src/ImageSharp.Drawing/Processing/PatternBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/PatternBrush{TPixel}.cs @@ -97,7 +97,11 @@ namespace SixLabors.ImageSharp.Processing RectangleF region, GraphicsOptions options) where TPixel : struct, IPixel => - new PatternBrushApplicator(source, this.pattern, this.patternVector, options); + new PatternBrushApplicator( + source, + this.pattern.ToPixelMatrix(source.Configuration), + this.patternVector, + options); /// /// The pattern brush applicator. @@ -118,11 +122,10 @@ namespace SixLabors.ImageSharp.Processing /// The pattern. /// The patternVector. /// The options - public PatternBrushApplicator(ImageFrame source, in DenseMatrix pattern, DenseMatrix patternVector, GraphicsOptions options) + public PatternBrushApplicator(ImageFrame source, in DenseMatrix pattern, DenseMatrix patternVector, GraphicsOptions options) : base(source, options) { - this.pattern = new DenseMatrix(pattern.Columns, pattern.Rows); - Color.ToPixel(source.Configuration, pattern.Data, this.pattern.Data); + this.pattern = pattern; this.patternVector = patternVector; } diff --git a/src/ImageSharp.Drawing/Processing/Pens.cs b/src/ImageSharp.Drawing/Processing/Pens.cs index 90253a3cb8..4b8f478c92 100644 --- a/src/ImageSharp.Drawing/Processing/Pens.cs +++ b/src/ImageSharp.Drawing/Processing/Pens.cs @@ -21,109 +21,79 @@ namespace SixLabors.ImageSharp.Processing /// /// The color. /// The width. - /// The type of the color. /// The Pen - public static Pen Solid(TPixel color, float width) - where TPixel : struct, IPixel - => new Pen(color, width); + public static Pen Solid(Color color, float width) => new Pen(color, width); /// /// Create a solid pen with out any drawing patterns /// /// The brush. /// The width. - /// The type of the color. /// The Pen - public static Pen Solid(IBrush brush, float width) - where TPixel : struct, IPixel - => new Pen(brush, width); + public static Pen Solid(IBrush brush, float width) => new Pen(brush, width); /// /// Create a pen with a 'Dash' drawing patterns /// /// The color. /// The width. - /// The type of the color. /// The Pen - public static Pen Dash(TPixel color, float width) - where TPixel : struct, IPixel - => new Pen(color, width, DashedPattern); + public static Pen Dash(Color color, float width) => new Pen(color, width, DashedPattern); /// /// Create a pen with a 'Dash' drawing patterns /// /// The brush. /// The width. - /// The type of the color. /// The Pen - public static Pen Dash(IBrush brush, float width) - where TPixel : struct, IPixel - => new Pen(brush, width, DashedPattern); + public static Pen Dash(IBrush brush, float width) => new Pen(brush, width, DashedPattern); /// /// Create a pen with a 'Dot' drawing patterns /// /// The color. /// The width. - /// The type of the color. /// The Pen - public static Pen Dot(TPixel color, float width) - where TPixel : struct, IPixel - => new Pen(color, width, DottedPattern); + public static Pen Dot(Color color, float width) => new Pen(color, width, DottedPattern); /// /// Create a pen with a 'Dot' drawing patterns /// /// The brush. /// The width. - /// The type of the color. /// The Pen - public static Pen Dot(IBrush brush, float width) - where TPixel : struct, IPixel - => new Pen(brush, width, DottedPattern); + public static Pen Dot(IBrush brush, float width) => new Pen(brush, width, DottedPattern); /// /// Create a pen with a 'Dash Dot' drawing patterns /// /// The color. /// The width. - /// The type of the color. /// The Pen - public static Pen DashDot(TPixel color, float width) - where TPixel : struct, IPixel - => new Pen(color, width, DashDotPattern); + public static Pen DashDot(Color color, float width) => new Pen(color, width, DashDotPattern); /// /// Create a pen with a 'Dash Dot' drawing patterns /// /// The brush. /// The width. - /// The type of the color. /// The Pen - public static Pen DashDot(IBrush brush, float width) - where TPixel : struct, IPixel - => new Pen(brush, width, DashDotPattern); + public static Pen DashDot(IBrush brush, float width) => new Pen(brush, width, DashDotPattern); /// /// Create a pen with a 'Dash Dot Dot' drawing patterns /// /// The color. /// The width. - /// The type of the color. /// The Pen - public static Pen DashDotDot(TPixel color, float width) - where TPixel : struct, IPixel - => new Pen(color, width, DashDotDotPattern); + public static Pen DashDotDot(Color color, float width) => new Pen(color, width, DashDotDotPattern); /// /// Create a pen with a 'Dash Dot Dot' drawing patterns /// /// The brush. /// The width. - /// The type of the color. /// The Pen - public static Pen DashDotDot(IBrush brush, float width) - where TPixel : struct, IPixel - => new Pen(brush, width, DashDotDotPattern); + public static Pen DashDotDot(IBrush brush, float width) => new Pen(brush, width, DashDotDotPattern); } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/Pen{TPixel}.cs b/src/ImageSharp.Drawing/Processing/Pen{TPixel}.cs index 26c21a0e51..922394946a 100644 --- a/src/ImageSharp.Drawing/Processing/Pen{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/Pen{TPixel}.cs @@ -9,7 +9,6 @@ namespace SixLabors.ImageSharp.Processing /// /// Provides a pen that can apply a pattern to a line with a set brush and thickness /// - /// The type of the color. /// /// The pattern will be in to the form of new float[]{ 1f, 2f, 0.5f} this will be /// converted into a pattern that is 3.5 times longer that the width with 3 sections @@ -18,8 +17,7 @@ namespace SixLabors.ImageSharp.Processing /// section 3 will be width/2 long and will be filled /// the the pattern will immediately repeat without gap. /// - public class Pen : IPen - where TPixel : struct, IPixel + public class Pen : IPen { private readonly float[] pattern; @@ -29,8 +27,8 @@ namespace SixLabors.ImageSharp.Processing /// The color. /// The width. /// The pattern. - public Pen(TPixel color, float width, float[] pattern) - : this(new SolidBrush(color), width, pattern) + public Pen(Color color, float width, float[] pattern) + : this(new SolidBrush(color), width, pattern) { } @@ -40,7 +38,7 @@ namespace SixLabors.ImageSharp.Processing /// The brush. /// The width. /// The pattern. - public Pen(IBrush brush, float width, float[] pattern) + public Pen(IBrush brush, float width, float[] pattern) { this.StrokeFill = brush; this.StrokeWidth = width; @@ -52,8 +50,8 @@ namespace SixLabors.ImageSharp.Processing /// /// The color. /// The width. - public Pen(TPixel color, float width) - : this(new SolidBrush(color), width) + public Pen(Color color, float width) + : this(new SolidBrush(color), width) { } @@ -62,13 +60,13 @@ namespace SixLabors.ImageSharp.Processing /// /// The brush. /// The width. - public Pen(IBrush brush, float width) + public Pen(IBrush brush, float width) : this(brush, width, Pens.EmptyPattern) { } /// - public IBrush StrokeFill { get; } + public IBrush StrokeFill { get; } /// public float StrokeWidth { get; } diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs index ed6c869511..8ea1c48cc2 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs @@ -13,6 +13,26 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Drawing { + public class FillProcessor : IImageProcessor + { + public FillProcessor(IBrush brush, GraphicsOptions options) + { + this.Brush = brush; + this.Options = options; + } + + public IBrush Brush { get; } + + public GraphicsOptions Options { get; } + + /// + public IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel + { + return new FillProcessor(this); + } + } + /// /// Using the brush as a source of pixels colors blends the brush color with source. /// @@ -20,21 +40,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing internal class FillProcessor : ImageProcessor where TPixel : struct, IPixel { - /// - /// The brush. - /// - private readonly IBrush brush; - private readonly GraphicsOptions options; - - /// - /// Initializes a new instance of the class. - /// - /// The brush to source pixel colors from. - /// The options - public FillProcessor(IBrush brush, GraphicsOptions options) + private readonly FillProcessor definition; + + public FillProcessor(FillProcessor definition) { - this.brush = brush; - this.options = options; + this.definition = definition; } /// @@ -55,11 +65,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY); + IBrush brush = this.definition.Brush; + GraphicsOptions options = this.definition.Options; + // If there's no reason for blending, then avoid it. - if (this.IsSolidBrushWithoutBlending(out SolidBrush solidBrush)) + if (this.IsSolidBrushWithoutBlending(out SolidBrush solidBrush)) { ParallelExecutionSettings parallelSettings = configuration.GetParallelSettings().MultiplyMinimumPixelsPerTask(4); + TPixel colorPixel = solidBrush.Color.ToPixel(); + ParallelHelper.IterateRows( workingRect, parallelSettings, @@ -67,7 +82,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing { for (int y = rows.Min; y < rows.Max; y++) { - source.GetPixelRowSpan(y).Slice(minX, width).Fill(solidBrush.Color); + source.GetPixelRowSpan(y).Slice(minX, width).Fill(colorPixel); } }); } @@ -85,10 +100,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing } using (IMemoryOwner amount = source.MemoryAllocator.Allocate(width)) - using (BrushApplicator applicator = this.brush.CreateApplicator( + using (BrushApplicator applicator = brush.CreateApplicator( source, sourceRectangle, - this.options)) + options)) { amount.GetSpan().Fill(1f); @@ -109,16 +124,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing } } - private bool IsSolidBrushWithoutBlending(out SolidBrush solidBrush) + private bool IsSolidBrushWithoutBlending(out SolidBrush solidBrush) { - solidBrush = this.brush as SolidBrush; + solidBrush = this.definition.Brush as SolidBrush; if (solidBrush == null) { return false; } - return this.options.IsOpaqueColorWithoutBlending(solidBrush.Color); + return this.definition.Options.IsOpaqueColorWithoutBlending(solidBrush.Color); } } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs index 550c021caa..3653102efa 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs @@ -13,24 +13,15 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Drawing { - /// - /// Using a brush and a shape fills shape with contents of brush the - /// - /// The type of the color. - /// - internal class FillRegionProcessor : ImageProcessor - where TPixel : struct, IPixel + public class FillRegionProcessor : IImageProcessor { - private const float AntialiasFactor = 1f; - private const int DrawPadding = 1; - /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The details how to fill the region of interest. /// The region of interest to be filled. /// The configuration options. - public FillRegionProcessor(IBrush brush, Region region, GraphicsOptions options) + public FillRegionProcessor(IBrush brush, Region region, GraphicsOptions options) { this.Region = region; this.Brush = brush; @@ -40,7 +31,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing /// /// Gets the brush. /// - public IBrush Brush { get; } + public IBrush Brush { get; } /// /// Gets the region that this processor applies to. @@ -55,10 +46,35 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing /// public GraphicsOptions Options { get; } + /// + public IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel + { + return new FillRegionProcessor(this); + } + } + + /// + /// Using a brush and a shape fills shape with contents of brush the + /// + /// The type of the color. + /// + internal class FillRegionProcessor : ImageProcessor + where TPixel : struct, IPixel + { + private readonly FillRegionProcessor definition; + + public FillRegionProcessor(FillRegionProcessor definition) + { + this.definition = definition; + } + /// protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - Region region = this.Region; + GraphicsOptions options = this.definition.Options; + IBrush brush = this.definition.Brush; + Region region = this.definition.Region; Rectangle rect = region.Bounds; // Align start/end positions. @@ -84,17 +100,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing // and this can cause missed fills when not using antialiasing.so we offset the pixel grid by 0.5 in the x & y direction thus causing the# // region to align with the pixel grid. float offset = 0.5f; - if (this.Options.Antialias) + if (options.Antialias) { offset = 0f; // we are antialiasing skip offsetting as real antialiasing should take care of offset. - subpixelCount = this.Options.AntialiasSubpixelDepth; + subpixelCount = options.AntialiasSubpixelDepth; if (subpixelCount < 4) { subpixelCount = 4; } } - using (BrushApplicator applicator = this.Brush.CreateApplicator(source, rect, this.Options)) + using (BrushApplicator applicator = brush.CreateApplicator(source, rect, options)) { int scanlineWidth = maxX - minX; using (IMemoryOwner bBuffer = source.MemoryAllocator.Allocate(maxIntersections)) @@ -107,7 +123,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing Span buffer = bBuffer.GetSpan(); Span scanline = bScanline.GetSpan(); - bool isSolidBrushWithoutBlending = this.IsSolidBrushWithoutBlending(out SolidBrush solidBrush); + bool isSolidBrushWithoutBlending = this.IsSolidBrushWithoutBlending(out SolidBrush solidBrush); + TPixel solidBrushColor = isSolidBrushWithoutBlending ? solidBrush.Color.ToPixel() : default; for (int y = minY; y < maxY; y++) { @@ -168,7 +185,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing if (scanlineDirty) { - if (!this.Options.Antialias) + if (!options.Antialias) { bool hasOnes = false; bool hasZeros = false; @@ -190,7 +207,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing { if (hasOnes) { - source.GetPixelRowSpan(y).Slice(minX, scanlineWidth).Fill(solidBrush.Color); + source.GetPixelRowSpan(y).Slice(minX, scanlineWidth).Fill(solidBrushColor); } continue; @@ -204,16 +221,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing } } - private bool IsSolidBrushWithoutBlending(out SolidBrush solidBrush) + private bool IsSolidBrushWithoutBlending(out SolidBrush solidBrush) { - solidBrush = this.Brush as SolidBrush; + solidBrush = this.definition.Brush as SolidBrush; if (solidBrush == null) { return false; } - return this.Options.IsOpaqueColorWithoutBlending(solidBrush.Color); + return this.definition.Options.IsOpaqueColorWithoutBlending(solidBrush.Color); } } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs b/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs index 266d842bfa..016cf56dea 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs @@ -15,17 +15,10 @@ using SixLabors.Shapes; namespace SixLabors.ImageSharp.Processing.Processors.Text { - /// - /// Using the brush as a source of pixels colors blends the brush color with source. - /// - /// The pixel format. - internal class DrawTextProcessor : ImageProcessor - where TPixel : struct, IPixel + public class DrawTextProcessor : IImageProcessor { - private CachingGlyphRenderer textRenderer; - /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The options /// The text we want to render @@ -33,7 +26,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text /// The brush to source pixel colors from. /// The pen to outline text with. /// The location on the image to start drawing the text from. - public DrawTextProcessor(TextGraphicsOptions options, string text, Font font, IBrush brush, IPen pen, PointF location) + public DrawTextProcessor(TextGraphicsOptions options, string text, Font font, IBrush brush, IPen pen, PointF location) { Guard.NotNull(text, nameof(text)); Guard.NotNull(font, nameof(font)); @@ -54,7 +47,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text /// /// Gets the brush. /// - public IBrush Brush { get; } + public IBrush Brush { get; } /// /// Gets the options @@ -69,7 +62,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text /// /// Gets the pen used for outlining the text, if Null then we will not outline /// - public IPen Pen { get; } + public IPen Pen { get; } /// /// Gets the font used to render the text. @@ -81,6 +74,42 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text /// public PointF Location { get; } + /// + public IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel + { + throw new NotImplementedException(); + } + } + + /// + /// Using the brush as a source of pixels colors blends the brush color with source. + /// + /// The pixel format. + internal class DrawTextProcessor : ImageProcessor + where TPixel : struct, IPixel + { + private CachingGlyphRenderer textRenderer; + + private readonly DrawTextProcessor definition; + + public DrawTextProcessor(DrawTextProcessor definition) + { + this.definition = definition; + } + + private TextGraphicsOptions Options => this.definition.Options; + + private Font Font => this.definition.Font; + + private PointF Location => this.definition.Location; + + private string Text => this.definition.Text; + + private IPen Pen => this.definition.Pen; + + private IBrush Brush => this.definition.Brush; + protected override void BeforeImageApply(Image source, Rectangle sourceRectangle) { base.BeforeImageApply(source, sourceRectangle); @@ -115,7 +144,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text Draw(this.textRenderer.FillOperations, this.Brush); Draw(this.textRenderer.OutlineOperations, this.Pen?.StrokeFill); - void Draw(List operations, IBrush brush) + void Draw(List operations, IBrush brush) { if (operations?.Count > 0) { diff --git a/src/ImageSharp.Drawing/Processing/RecolorBrush{TPixel}.cs b/src/ImageSharp.Drawing/Processing/RecolorBrush{TPixel}.cs index 09a1ff71fb..ff098dce7c 100644 --- a/src/ImageSharp.Drawing/Processing/RecolorBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/RecolorBrush{TPixel}.cs @@ -16,16 +16,15 @@ namespace SixLabors.ImageSharp.Processing /// Provides an implementation of a brush that can recolor an image /// /// The pixel format. - public class RecolorBrush : IBrush - where TPixel : struct, IPixel + public class RecolorBrush : IBrush { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Color of the source. /// Color of the target. /// The threshold as a value between 0 and 1. - public RecolorBrush(TPixel sourceColor, TPixel targetColor, float threshold) + public RecolorBrush(Color sourceColor, Color targetColor, float threshold) { this.SourceColor = sourceColor; this.Threshold = threshold; @@ -43,23 +42,33 @@ namespace SixLabors.ImageSharp.Processing /// /// The color of the source. /// - public TPixel SourceColor { get; } + public Color SourceColor { get; } /// /// Gets the target color. /// - public TPixel TargetColor { get; } + public Color TargetColor { get; } /// - public BrushApplicator CreateApplicator(ImageFrame source, RectangleF region, GraphicsOptions options) + public BrushApplicator CreateApplicator( + ImageFrame source, + RectangleF region, + GraphicsOptions options) + where TPixel : struct, IPixel { - return new RecolorBrushApplicator(source, this.SourceColor, this.TargetColor, this.Threshold, options); + return new RecolorBrushApplicator( + source, + this.SourceColor.ToPixel(), + this.TargetColor.ToPixel(), + this.Threshold, + options); } /// /// The recolor brush applicator. /// - private class RecolorBrushApplicator : BrushApplicator + private class RecolorBrushApplicator : BrushApplicator + where TPixel : struct, IPixel { /// /// The source color. @@ -79,7 +88,7 @@ namespace SixLabors.ImageSharp.Processing private readonly TPixel targetColorPixel; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The source image. /// Color of the source. diff --git a/src/ImageSharp/GraphicsOptions.cs b/src/ImageSharp/GraphicsOptions.cs index 2d20c17732..537418de24 100644 --- a/src/ImageSharp/GraphicsOptions.cs +++ b/src/ImageSharp/GraphicsOptions.cs @@ -1,141 +1,141 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp -{ - /// - /// Options for influencing the drawing functions. - /// - public struct GraphicsOptions - { - /// - /// Represents the default . - /// - public static readonly GraphicsOptions Default = new GraphicsOptions(true); - - private float? blendPercentage; - - private int? antialiasSubpixelDepth; - - private bool? antialias; - +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp +{ + /// + /// Options for influencing the drawing functions. + /// + public struct GraphicsOptions + { + /// + /// Represents the default . + /// + public static readonly GraphicsOptions Default = new GraphicsOptions(true); + + private float? blendPercentage; + + private int? antialiasSubpixelDepth; + + private bool? antialias; + private PixelColorBlendingMode colorBlendingMode; - private PixelAlphaCompositionMode alphaCompositionMode; - - /// - /// Initializes a new instance of the struct. - /// - /// If set to true [enable antialiasing]. - public GraphicsOptions(bool enableAntialiasing) - { + private PixelAlphaCompositionMode alphaCompositionMode; + + /// + /// Initializes a new instance of the struct. + /// + /// If set to true [enable antialiasing]. + public GraphicsOptions(bool enableAntialiasing) + { this.colorBlendingMode = PixelColorBlendingMode.Normal; - this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver; - this.blendPercentage = 1; - this.antialiasSubpixelDepth = 16; - this.antialias = enableAntialiasing; + this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver; + this.blendPercentage = 1; + this.antialiasSubpixelDepth = 16; + this.antialias = enableAntialiasing; } - /// - /// Initializes a new instance of the struct. - /// + /// + /// Initializes a new instance of the struct. + /// /// If set to true [enable antialiasing]. - /// blending percentage to apply to the drawing operation - public GraphicsOptions(bool enableAntialiasing, float opacity) + /// blending percentage to apply to the drawing operation + public GraphicsOptions(bool enableAntialiasing, float opacity) { Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity)); - + this.colorBlendingMode = PixelColorBlendingMode.Normal; - this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver; - this.blendPercentage = opacity; - this.antialiasSubpixelDepth = 16; - this.antialias = enableAntialiasing; + this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver; + this.blendPercentage = opacity; + this.antialiasSubpixelDepth = 16; + this.antialias = enableAntialiasing; } - /// - /// Initializes a new instance of the struct. - /// + /// + /// Initializes a new instance of the struct. + /// /// If set to true [enable antialiasing]. /// blending percentage to apply to the drawing operation - /// color blending mode to apply to the drawing operation - public GraphicsOptions(bool enableAntialiasing, PixelColorBlendingMode blending, float opacity) + /// color blending mode to apply to the drawing operation + public GraphicsOptions(bool enableAntialiasing, PixelColorBlendingMode blending, float opacity) { Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity)); - + this.colorBlendingMode = blending; - this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver; - this.blendPercentage = opacity; - this.antialiasSubpixelDepth = 16; - this.antialias = enableAntialiasing; + this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver; + this.blendPercentage = opacity; + this.antialiasSubpixelDepth = 16; + this.antialias = enableAntialiasing; } - /// - /// Initializes a new instance of the struct. - /// + /// + /// Initializes a new instance of the struct. + /// /// If set to true [enable antialiasing]. /// blending percentage to apply to the drawing operation /// color blending mode to apply to the drawing operation - /// alpha composition mode to apply to the drawing operation - public GraphicsOptions(bool enableAntialiasing, PixelColorBlendingMode blending, PixelAlphaCompositionMode composition, float opacity) + /// alpha composition mode to apply to the drawing operation + public GraphicsOptions(bool enableAntialiasing, PixelColorBlendingMode blending, PixelAlphaCompositionMode composition, float opacity) { Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity)); - + this.colorBlendingMode = blending; - this.alphaCompositionMode = composition; - this.blendPercentage = opacity; - this.antialiasSubpixelDepth = 16; - this.antialias = enableAntialiasing; - } - - /// - /// Gets or sets a value indicating whether antialiasing should be applied. - /// - public bool Antialias - { - get => this.antialias ?? true; - set => this.antialias = value; - } - - /// - /// Gets or sets a value indicating the number of subpixels to use while rendering with antialiasing enabled. - /// - public int AntialiasSubpixelDepth - { - get => this.antialiasSubpixelDepth ?? 16; - set => this.antialiasSubpixelDepth = value; - } - - /// - /// Gets or sets a value indicating the blending percentage to apply to the drawing operation - /// - public float BlendPercentage - { - get => (this.blendPercentage ?? 1).Clamp(0, 1); - set => this.blendPercentage = value; - } - - // In the future we could expose a PixelBlender directly on here - // or some forms of PixelBlender factory for each pixel type. Will need - // some API thought post V1. - - /// - /// Gets or sets a value indicating the color blending mode to apply to the drawing operation - /// - public PixelColorBlendingMode ColorBlendingMode - { - get => this.colorBlendingMode; - set => this.colorBlendingMode = value; + this.alphaCompositionMode = composition; + this.blendPercentage = opacity; + this.antialiasSubpixelDepth = 16; + this.antialias = enableAntialiasing; } - /// - /// Gets or sets a value indicating the alpha composition mode to apply to the drawing operation - /// - public PixelAlphaCompositionMode AlphaCompositionMode - { - get => this.alphaCompositionMode; - set => this.alphaCompositionMode = value; + /// + /// Gets or sets a value indicating whether antialiasing should be applied. + /// + public bool Antialias + { + get => this.antialias ?? true; + set => this.antialias = value; + } + + /// + /// Gets or sets a value indicating the number of subpixels to use while rendering with antialiasing enabled. + /// + public int AntialiasSubpixelDepth + { + get => this.antialiasSubpixelDepth ?? 16; + set => this.antialiasSubpixelDepth = value; + } + + /// + /// Gets or sets a value indicating the blending percentage to apply to the drawing operation + /// + public float BlendPercentage + { + get => (this.blendPercentage ?? 1).Clamp(0, 1); + set => this.blendPercentage = value; + } + + // In the future we could expose a PixelBlender directly on here + // or some forms of PixelBlender factory for each pixel type. Will need + // some API thought post V1. + + /// + /// Gets or sets a value indicating the color blending mode to apply to the drawing operation + /// + public PixelColorBlendingMode ColorBlendingMode + { + get => this.colorBlendingMode; + set => this.colorBlendingMode = value; + } + + /// + /// Gets or sets a value indicating the alpha composition mode to apply to the drawing operation + /// + public PixelAlphaCompositionMode AlphaCompositionMode + { + get => this.alphaCompositionMode; + set => this.alphaCompositionMode = value; } /// @@ -149,8 +149,7 @@ namespace SixLabors.ImageSharp /// filling with a solid color, the blending can be avoided by a plain color replacement. /// This method can be useful for such processors to select the fast path. /// - internal bool IsOpaqueColorWithoutBlending(TPixel color) - where TPixel : struct, IPixel + internal bool IsOpaqueColorWithoutBlending(Color color) { if (this.ColorBlendingMode != PixelColorBlendingMode.Normal) { @@ -174,6 +173,6 @@ namespace SixLabors.ImageSharp } return true; - } - } + } + } } \ No newline at end of file From 6672741c456428e350b3b908777c26f85df6ebba Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 12 May 2019 21:27:04 +0200 Subject: [PATCH 180/223] ImageSharp.Drawing compiles --- .../Processing/ColorStop{TPixel}.cs | 10 +- .../Processing/DrawBezierExtensions.cs | 61 +++++++----- .../Processing/DrawLineExtensions.cs | 61 +++++++----- .../DrawPathCollectionExtensions.cs | 57 ++++++----- .../Processing/DrawPathExtensions.cs | 44 ++++----- .../Processing/DrawPolygonExtensions.cs | 61 +++++++----- .../Processing/DrawRectangleExtensions.cs | 58 +++++++----- .../Processing/DrawTextExtensions.cs | 94 ++++++++++++------- .../EllipticGradientBrush{TPixel}.cs | 21 ++--- .../Processing/FillPathBuilderExtensions.cs | 36 ++++--- .../FillPathCollectionExtensions.cs | 36 ++++--- .../Processing/FillPathExtensions.cs | 28 +++--- .../Processing/FillPolygonExtensions.cs | 38 ++++---- .../Processing/FillRectangleExtensions.cs | 34 +++---- .../Processing/FillRegionExtensions.cs | 52 +++++----- .../Processing/GradientBrushBase{TPixel}.cs | 38 ++++---- .../Processing/LinearGradientBrush{TPixel}.cs | 19 ++-- .../Processing/PatternBrush{TPixel}.cs | 7 +- .../Processing/RadialGradientBrush{TPixel}.cs | 22 ++--- src/ImageSharp/GraphicsOptions.cs | 1 - 20 files changed, 442 insertions(+), 336 deletions(-) diff --git a/src/ImageSharp.Drawing/Processing/ColorStop{TPixel}.cs b/src/ImageSharp.Drawing/Processing/ColorStop{TPixel}.cs index 7fd0ba7cd3..aee559469f 100644 --- a/src/ImageSharp.Drawing/Processing/ColorStop{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/ColorStop{TPixel}.cs @@ -10,17 +10,15 @@ namespace SixLabors.ImageSharp.Processing /// /// A struct that defines a single color stop. /// - /// The pixel format. [DebuggerDisplay("ColorStop({Ratio} -> {Color}")] - public struct ColorStop - where TPixel : struct, IPixel + public readonly struct ColorStop { /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the struct. /// /// Where should it be? 0 is at the start, 1 at the end of the Gradient. /// What color should be used at that point? - public ColorStop(float ratio, TPixel color) + public ColorStop(float ratio, in Color color) { this.Ratio = ratio; this.Color = color; @@ -34,6 +32,6 @@ namespace SixLabors.ImageSharp.Processing /// /// Gets the color to be used. /// - public TPixel Color { get; } + public Color Color { get; } } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/DrawBezierExtensions.cs b/src/ImageSharp.Drawing/Processing/DrawBezierExtensions.cs index 991c01293a..85addd5f2c 100644 --- a/src/ImageSharp.Drawing/Processing/DrawBezierExtensions.cs +++ b/src/ImageSharp.Drawing/Processing/DrawBezierExtensions.cs @@ -15,80 +15,93 @@ namespace SixLabors.ImageSharp.Processing /// /// Draws the provided points as an open Bezier path at the provided thickness with the supplied brush /// - /// The image this method extends. /// The options. /// The brush. /// The thickness. /// The points. /// The . - public static IImageProcessingContext DrawBeziers(this IImageProcessingContext source, GraphicsOptions options, IBrush brush, float thickness, params PointF[] points) - - => source.Draw(options, new Pen(brush, thickness), new Path(new CubicBezierLineSegment(points))); + public static IImageProcessingContext DrawBeziers( + this IImageProcessingContext source, + GraphicsOptions options, + IBrush brush, + float thickness, + params PointF[] points) => + source.Draw(options, new Pen(brush, thickness), new Path(new CubicBezierLineSegment(points))); /// /// Draws the provided points as an open Bezier path at the provided thickness with the supplied brush /// - /// The image this method extends. /// The brush. /// The thickness. /// The points. /// The . - public static IImageProcessingContext DrawBeziers(this IImageProcessingContext source, IBrush brush, float thickness, params PointF[] points) - - => source.Draw(new Pen(brush, thickness), new Path(new CubicBezierLineSegment(points))); + public static IImageProcessingContext DrawBeziers( + this IImageProcessingContext source, + IBrush brush, + float thickness, + params PointF[] points) => + source.Draw(new Pen(brush, thickness), new Path(new CubicBezierLineSegment(points))); /// /// Draws the provided points as an open Bezier path at the provided thickness with the supplied brush /// - /// The image this method extends. /// The color. /// The thickness. /// The points. /// The . - public static IImageProcessingContext DrawBeziers(this IImageProcessingContext source, TPixel color, float thickness, params PointF[] points) - - => source.DrawBeziers(new SolidBrush(color), thickness, points); + public static IImageProcessingContext DrawBeziers( + this IImageProcessingContext source, + Color color, + float thickness, + params PointF[] points) => + source.DrawBeziers(new SolidBrush(color), thickness, points); /// /// Draws the provided points as an open Bezier path at the provided thickness with the supplied brush /// - /// The image this method extends. /// The options. /// The color. /// The thickness. /// The points. /// The . - public static IImageProcessingContext DrawBeziers(this IImageProcessingContext source, GraphicsOptions options, TPixel color, float thickness, params PointF[] points) - - => source.DrawBeziers(options, new SolidBrush(color), thickness, points); + public static IImageProcessingContext DrawBeziers( + this IImageProcessingContext source, + GraphicsOptions options, + Color color, + float thickness, + params PointF[] points) => + source.DrawBeziers(options, new SolidBrush(color), thickness, points); /// /// Draws the provided points as an open Bezier path with the supplied pen /// - /// The image this method extends. /// The options. /// The pen. /// The points. /// The . - public static IImageProcessingContext DrawBeziers(this IImageProcessingContext source, GraphicsOptions options, IPen pen, params PointF[] points) - - => source.Draw(options, pen, new Path(new CubicBezierLineSegment(points))); + public static IImageProcessingContext DrawBeziers( + this IImageProcessingContext source, + GraphicsOptions options, + IPen pen, + params PointF[] points) => + source.Draw(options, pen, new Path(new CubicBezierLineSegment(points))); /// /// Draws the provided points as an open Bezier path with the supplied pen /// - /// The image this method extends. /// The pen. /// The points. /// The . - public static IImageProcessingContext DrawBeziers(this IImageProcessingContext source, IPen pen, params PointF[] points) - - => source.Draw(pen, new Path(new CubicBezierLineSegment(points))); + public static IImageProcessingContext DrawBeziers( + this IImageProcessingContext source, + IPen pen, + params PointF[] points) => + source.Draw(pen, new Path(new CubicBezierLineSegment(points))); } } diff --git a/src/ImageSharp.Drawing/Processing/DrawLineExtensions.cs b/src/ImageSharp.Drawing/Processing/DrawLineExtensions.cs index 9084b30efe..b7b419be61 100644 --- a/src/ImageSharp.Drawing/Processing/DrawLineExtensions.cs +++ b/src/ImageSharp.Drawing/Processing/DrawLineExtensions.cs @@ -15,80 +15,93 @@ namespace SixLabors.ImageSharp.Processing /// /// Draws the provided Points as an open Linear path at the provided thickness with the supplied brush /// - /// The type of the color. /// The image this method extends. /// The options. /// The brush. /// The thickness. /// The points. /// The . - public static IImageProcessingContext DrawLines(this IImageProcessingContext source, GraphicsOptions options, IBrush brush, float thickness, params PointF[] points) - where TPixel : struct, IPixel - => source.Draw(options, new Pen(brush, thickness), new Path(new LinearLineSegment(points))); + public static IImageProcessingContext DrawLines( + this IImageProcessingContext source, + GraphicsOptions options, + IBrush brush, + float thickness, + params PointF[] points) => + source.Draw(options, new Pen(brush, thickness), new Path(new LinearLineSegment(points))); /// /// Draws the provided Points as an open Linear path at the provided thickness with the supplied brush /// - /// The type of the color. /// The image this method extends. /// The brush. /// The thickness. /// The points. /// The . - public static IImageProcessingContext DrawLines(this IImageProcessingContext source, IBrush brush, float thickness, params PointF[] points) - where TPixel : struct, IPixel - => source.Draw(new Pen(brush, thickness), new Path(new LinearLineSegment(points))); + public static IImageProcessingContext DrawLines( + this IImageProcessingContext source, + IBrush brush, + float thickness, + params PointF[] points) => + source.Draw(new Pen(brush, thickness), new Path(new LinearLineSegment(points))); /// /// Draws the provided Points as an open Linear path at the provided thickness with the supplied brush /// - /// The type of the color. /// The image this method extends. /// The color. /// The thickness. /// The points. /// The . - public static IImageProcessingContext DrawLines(this IImageProcessingContext source, TPixel color, float thickness, params PointF[] points) - where TPixel : struct, IPixel - => source.DrawLines(new SolidBrush(color), thickness, points); + public static IImageProcessingContext DrawLines( + this IImageProcessingContext source, + Color color, + float thickness, + params PointF[] points) => + source.DrawLines(new SolidBrush(color), thickness, points); /// /// Draws the provided Points as an open Linear path at the provided thickness with the supplied brush /// - /// The type of the color. /// The image this method extends. /// The options. /// The color. /// The thickness. /// The points. /// The .> - public static IImageProcessingContext DrawLines(this IImageProcessingContext source, GraphicsOptions options, TPixel color, float thickness, params PointF[] points) - where TPixel : struct, IPixel - => source.DrawLines(options, new SolidBrush(color), thickness, points); + public static IImageProcessingContext DrawLines( + this IImageProcessingContext source, + GraphicsOptions options, + Color color, + float thickness, + params PointF[] points) => + source.DrawLines(options, new SolidBrush(color), thickness, points); /// /// Draws the provided Points as an open Linear path with the supplied pen /// - /// The type of the color. /// The image this method extends. /// The options. /// The pen. /// The points. /// The . - public static IImageProcessingContext DrawLines(this IImageProcessingContext source, GraphicsOptions options, IPen pen, params PointF[] points) - where TPixel : struct, IPixel - => source.Draw(options, pen, new Path(new LinearLineSegment(points))); + public static IImageProcessingContext DrawLines( + this IImageProcessingContext source, + GraphicsOptions options, + IPen pen, + params PointF[] points) => + source.Draw(options, pen, new Path(new LinearLineSegment(points))); /// /// Draws the provided Points as an open Linear path with the supplied pen /// - /// The type of the color. /// The image this method extends. /// The pen. /// The points. /// The . - public static IImageProcessingContext DrawLines(this IImageProcessingContext source, IPen pen, params PointF[] points) - where TPixel : struct, IPixel - => source.Draw(pen, new Path(new LinearLineSegment(points))); + public static IImageProcessingContext DrawLines( + this IImageProcessingContext source, + IPen pen, + params PointF[] points) => + source.Draw(pen, new Path(new LinearLineSegment(points))); } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/DrawPathCollectionExtensions.cs b/src/ImageSharp.Drawing/Processing/DrawPathCollectionExtensions.cs index 0d3abf297e..79b4fd0a80 100644 --- a/src/ImageSharp.Drawing/Processing/DrawPathCollectionExtensions.cs +++ b/src/ImageSharp.Drawing/Processing/DrawPathCollectionExtensions.cs @@ -14,14 +14,16 @@ namespace SixLabors.ImageSharp.Processing /// /// Draws the outline of the polygon with the provided pen. /// - /// The type of the color. /// The image this method extends. /// The options. /// The pen. /// The paths. /// The . - public static IImageProcessingContext Draw(this IImageProcessingContext source, GraphicsOptions options, IPen pen, IPathCollection paths) - where TPixel : struct, IPixel + public static IImageProcessingContext Draw( + this IImageProcessingContext source, + GraphicsOptions options, + IPen pen, + IPathCollection paths) { foreach (IPath path in paths) { @@ -34,67 +36,76 @@ namespace SixLabors.ImageSharp.Processing /// /// Draws the outline of the polygon with the provided pen. /// - /// The type of the color. /// The image this method extends. /// The pen. /// The paths. /// The . - public static IImageProcessingContext Draw(this IImageProcessingContext source, IPen pen, IPathCollection paths) - where TPixel : struct, IPixel - => source.Draw(GraphicsOptions.Default, pen, paths); + public static IImageProcessingContext + Draw(this IImageProcessingContext source, IPen pen, IPathCollection paths) => + source.Draw(GraphicsOptions.Default, pen, paths); /// /// Draws the outline of the polygon with the provided brush at the provided thickness. /// - /// The type of the color. /// The image this method extends. /// The options. /// The brush. /// The thickness. /// The shapes. /// The . - public static IImageProcessingContext Draw(this IImageProcessingContext source, GraphicsOptions options, IBrush brush, float thickness, IPathCollection paths) - where TPixel : struct, IPixel - => source.Draw(options, new Pen(brush, thickness), paths); + public static IImageProcessingContext Draw( + this IImageProcessingContext source, + GraphicsOptions options, + IBrush brush, + float thickness, + IPathCollection paths) => + source.Draw(options, new Pen(brush, thickness), paths); /// /// Draws the outline of the polygon with the provided brush at the provided thickness. /// - /// The type of the color. /// The image this method extends. /// The brush. /// The thickness. /// The paths. /// The . - public static IImageProcessingContext Draw(this IImageProcessingContext source, IBrush brush, float thickness, IPathCollection paths) - where TPixel : struct, IPixel - => source.Draw(new Pen(brush, thickness), paths); + public static IImageProcessingContext Draw( + this IImageProcessingContext source, + IBrush brush, + float thickness, + IPathCollection paths) => + source.Draw(new Pen(brush, thickness), paths); /// /// Draws the outline of the polygon with the provided brush at the provided thickness. /// - /// The type of the color. /// The image this method extends. /// The options. /// The color. /// The thickness. /// The paths. /// The . - public static IImageProcessingContext Draw(this IImageProcessingContext source, GraphicsOptions options, TPixel color, float thickness, IPathCollection paths) - where TPixel : struct, IPixel - => source.Draw(options, new SolidBrush(color), thickness, paths); + public static IImageProcessingContext Draw( + this IImageProcessingContext source, + GraphicsOptions options, + Color color, + float thickness, + IPathCollection paths) => + source.Draw(options, new SolidBrush(color), thickness, paths); /// /// Draws the outline of the polygon with the provided brush at the provided thickness. /// - /// The type of the color. /// The image this method extends. /// The color. /// The thickness. /// The paths. /// The . - public static IImageProcessingContext Draw(this IImageProcessingContext source, TPixel color, float thickness, IPathCollection paths) - where TPixel : struct, IPixel - => source.Draw(new SolidBrush(color), thickness, paths); + public static IImageProcessingContext Draw( + this IImageProcessingContext source, + Color color, + float thickness, + IPathCollection paths) => + source.Draw(new SolidBrush(color), thickness, paths); } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/DrawPathExtensions.cs b/src/ImageSharp.Drawing/Processing/DrawPathExtensions.cs index 4dbe942f2b..f231f1baae 100644 --- a/src/ImageSharp.Drawing/Processing/DrawPathExtensions.cs +++ b/src/ImageSharp.Drawing/Processing/DrawPathExtensions.cs @@ -15,80 +15,80 @@ namespace SixLabors.ImageSharp.Processing /// /// Draws the outline of the polygon with the provided pen. /// - /// The type of the color. + /// The image this method extends. /// The options. /// The pen. /// The path. /// The . - public static IImageProcessingContext Draw(this IImageProcessingContext source, GraphicsOptions options, IPen pen, IPath path) - where TPixel : struct, IPixel + public static IImageProcessingContext Draw(this IImageProcessingContext source, GraphicsOptions options, IPen pen, IPath path) + => source.Fill(options, pen.StrokeFill, new ShapePath(path, pen)); /// /// Draws the outline of the polygon with the provided pen. /// - /// The type of the color. + /// The image this method extends. /// The pen. /// The path. /// The . - public static IImageProcessingContext Draw(this IImageProcessingContext source, IPen pen, IPath path) - where TPixel : struct, IPixel + public static IImageProcessingContext Draw(this IImageProcessingContext source, IPen pen, IPath path) + => source.Draw(GraphicsOptions.Default, pen, path); /// /// Draws the outline of the polygon with the provided brush at the provided thickness. /// - /// The type of the color. + /// The image this method extends. /// The options. /// The brush. /// The thickness. /// The shape. /// The . - public static IImageProcessingContext Draw(this IImageProcessingContext source, GraphicsOptions options, IBrush brush, float thickness, IPath path) - where TPixel : struct, IPixel - => source.Draw(options, new Pen(brush, thickness), path); + public static IImageProcessingContext Draw(this IImageProcessingContext source, GraphicsOptions options, IBrush brush, float thickness, IPath path) + + => source.Draw(options, new Pen(brush, thickness), path); /// /// Draws the outline of the polygon with the provided brush at the provided thickness. /// - /// The type of the color. + /// The image this method extends. /// The brush. /// The thickness. /// The path. /// The . - public static IImageProcessingContext Draw(this IImageProcessingContext source, IBrush brush, float thickness, IPath path) - where TPixel : struct, IPixel - => source.Draw(new Pen(brush, thickness), path); + public static IImageProcessingContext Draw(this IImageProcessingContext source, IBrush brush, float thickness, IPath path) + + => source.Draw(new Pen(brush, thickness), path); /// /// Draws the outline of the polygon with the provided brush at the provided thickness. /// - /// The type of the color. + /// The image this method extends. /// The options. /// The color. /// The thickness. /// The path. /// The . - public static IImageProcessingContext Draw(this IImageProcessingContext source, GraphicsOptions options, TPixel color, float thickness, IPath path) - where TPixel : struct, IPixel - => source.Draw(options, new SolidBrush(color), thickness, path); + public static IImageProcessingContext Draw(this IImageProcessingContext source, GraphicsOptions options, Color color, float thickness, IPath path) + + => source.Draw(options, new SolidBrush(color), thickness, path); /// /// Draws the outline of the polygon with the provided brush at the provided thickness. /// - /// The type of the color. + /// The image this method extends. /// The color. /// The thickness. /// The path. /// The . - public static IImageProcessingContext Draw(this IImageProcessingContext source, TPixel color, float thickness, IPath path) - where TPixel : struct, IPixel - => source.Draw(new SolidBrush(color), thickness, path); + public static IImageProcessingContext Draw(this IImageProcessingContext source, Color color, float thickness, IPath path) + + => source.Draw(new SolidBrush(color), thickness, path); } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/DrawPolygonExtensions.cs b/src/ImageSharp.Drawing/Processing/DrawPolygonExtensions.cs index 4dcfe00aa3..324e05e87b 100644 --- a/src/ImageSharp.Drawing/Processing/DrawPolygonExtensions.cs +++ b/src/ImageSharp.Drawing/Processing/DrawPolygonExtensions.cs @@ -15,80 +15,93 @@ namespace SixLabors.ImageSharp.Processing /// /// Draws the provided Points as a closed Linear Polygon with the provided brush at the provided thickness. /// - /// The type of the color. /// The image this method extends. /// The options. /// The brush. /// The thickness. /// The points. /// The . - public static IImageProcessingContext DrawPolygon(this IImageProcessingContext source, GraphicsOptions options, IBrush brush, float thickness, params PointF[] points) - where TPixel : struct, IPixel - => source.Draw(options, new Pen(brush, thickness), new Polygon(new LinearLineSegment(points))); + public static IImageProcessingContext DrawPolygon( + this IImageProcessingContext source, + GraphicsOptions options, + IBrush brush, + float thickness, + params PointF[] points) => + source.Draw(options, new Pen(brush, thickness), new Polygon(new LinearLineSegment(points))); /// /// Draws the provided Points as a closed Linear Polygon with the provided brush at the provided thickness. /// - /// The type of the color. /// The image this method extends. /// The brush. /// The thickness. /// The points. /// The . - public static IImageProcessingContext DrawPolygon(this IImageProcessingContext source, IBrush brush, float thickness, params PointF[] points) - where TPixel : struct, IPixel - => source.Draw(new Pen(brush, thickness), new Polygon(new LinearLineSegment(points))); + public static IImageProcessingContext DrawPolygon( + this IImageProcessingContext source, + IBrush brush, + float thickness, + params PointF[] points) => + source.Draw(new Pen(brush, thickness), new Polygon(new LinearLineSegment(points))); /// /// Draws the provided Points as a closed Linear Polygon with the provided brush at the provided thickness. /// - /// The type of the color. /// The image this method extends. /// The color. /// The thickness. /// The points. /// The . - public static IImageProcessingContext DrawPolygon(this IImageProcessingContext source, TPixel color, float thickness, params PointF[] points) - where TPixel : struct, IPixel - => source.DrawPolygon(new SolidBrush(color), thickness, points); + public static IImageProcessingContext DrawPolygon( + this IImageProcessingContext source, + Color color, + float thickness, + params PointF[] points) => + source.DrawPolygon(new SolidBrush(color), thickness, points); /// /// Draws the provided Points as a closed Linear Polygon with the provided brush at the provided thickness. /// - /// The type of the color. /// The image this method extends. /// The options. /// The color. /// The thickness. /// The points. /// The . - public static IImageProcessingContext DrawPolygon(this IImageProcessingContext source, GraphicsOptions options, TPixel color, float thickness, params PointF[] points) - where TPixel : struct, IPixel - => source.DrawPolygon(options, new SolidBrush(color), thickness, points); + public static IImageProcessingContext DrawPolygon( + this IImageProcessingContext source, + GraphicsOptions options, + Color color, + float thickness, + params PointF[] points) => + source.DrawPolygon(options, new SolidBrush(color), thickness, points); /// /// Draws the provided Points as a closed Linear Polygon with the provided Pen. /// - /// The type of the color. /// The image this method extends. /// The pen. /// The points. /// The . - public static IImageProcessingContext DrawPolygon(this IImageProcessingContext source, IPen pen, params PointF[] points) - where TPixel : struct, IPixel - => source.Draw(GraphicsOptions.Default, pen, new Polygon(new LinearLineSegment(points))); + public static IImageProcessingContext DrawPolygon( + this IImageProcessingContext source, + IPen pen, + params PointF[] points) => + source.Draw(GraphicsOptions.Default, pen, new Polygon(new LinearLineSegment(points))); /// /// Draws the provided Points as a closed Linear Polygon with the provided Pen. /// - /// The type of the color. /// The image this method extends. /// The options. /// The pen. /// The points. /// The . - public static IImageProcessingContext DrawPolygon(this IImageProcessingContext source, GraphicsOptions options, IPen pen, params PointF[] points) - where TPixel : struct, IPixel - => source.Draw(options, pen, new Polygon(new LinearLineSegment(points))); + public static IImageProcessingContext DrawPolygon( + this IImageProcessingContext source, + GraphicsOptions options, + IPen pen, + params PointF[] points) => + source.Draw(options, pen, new Polygon(new LinearLineSegment(points))); } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/DrawRectangleExtensions.cs b/src/ImageSharp.Drawing/Processing/DrawRectangleExtensions.cs index 918fb1e738..d0a6c8ccd2 100644 --- a/src/ImageSharp.Drawing/Processing/DrawRectangleExtensions.cs +++ b/src/ImageSharp.Drawing/Processing/DrawRectangleExtensions.cs @@ -15,80 +15,90 @@ namespace SixLabors.ImageSharp.Processing /// /// Draws the outline of the rectangle with the provided pen. /// - /// The type of the color. /// The image this method extends. /// The options. /// The pen. /// The shape. /// The . - public static IImageProcessingContext Draw(this IImageProcessingContext source, GraphicsOptions options, IPen pen, RectangleF shape) - where TPixel : struct, IPixel - => source.Draw(options, pen, new RectangularPolygon(shape.X, shape.Y, shape.Width, shape.Height)); + public static IImageProcessingContext Draw( + this IImageProcessingContext source, + GraphicsOptions options, + IPen pen, + RectangleF shape) => + source.Draw(options, pen, new RectangularPolygon(shape.X, shape.Y, shape.Width, shape.Height)); /// /// Draws the outline of the rectangle with the provided pen. /// - /// The type of the color. /// The image this method extends. /// The pen. /// The shape. /// The . - public static IImageProcessingContext Draw(this IImageProcessingContext source, IPen pen, RectangleF shape) - where TPixel : struct, IPixel - => source.Draw(GraphicsOptions.Default, pen, shape); + public static IImageProcessingContext Draw(this IImageProcessingContext source, IPen pen, RectangleF shape) => + source.Draw(GraphicsOptions.Default, pen, shape); /// /// Draws the outline of the rectangle with the provided brush at the provided thickness. /// - /// The type of the color. /// The image this method extends. /// The options. /// The brush. /// The thickness. /// The shape. /// The . - public static IImageProcessingContext Draw(this IImageProcessingContext source, GraphicsOptions options, IBrush brush, float thickness, RectangleF shape) - where TPixel : struct, IPixel - => source.Draw(options, new Pen(brush, thickness), shape); + public static IImageProcessingContext Draw( + this IImageProcessingContext source, + GraphicsOptions options, + IBrush brush, + float thickness, + RectangleF shape) => + source.Draw(options, new Pen(brush, thickness), shape); /// /// Draws the outline of the rectangle with the provided brush at the provided thickness. /// - /// The type of the color. /// The image this method extends. /// The brush. /// The thickness. /// The shape. /// The . - public static IImageProcessingContext Draw(this IImageProcessingContext source, IBrush brush, float thickness, RectangleF shape) - where TPixel : struct, IPixel - => source.Draw(new Pen(brush, thickness), shape); + public static IImageProcessingContext Draw( + this IImageProcessingContext source, + IBrush brush, + float thickness, + RectangleF shape) => + source.Draw(new Pen(brush, thickness), shape); /// /// Draws the outline of the rectangle with the provided brush at the provided thickness. /// - /// The type of the color. /// The image this method extends. /// The options. /// The color. /// The thickness. /// The shape. /// The . - public static IImageProcessingContext Draw(this IImageProcessingContext source, GraphicsOptions options, TPixel color, float thickness, RectangleF shape) - where TPixel : struct, IPixel - => source.Draw(options, new SolidBrush(color), thickness, shape); + public static IImageProcessingContext Draw( + this IImageProcessingContext source, + GraphicsOptions options, + Color color, + float thickness, + RectangleF shape) => + source.Draw(options, new SolidBrush(color), thickness, shape); /// /// Draws the outline of the rectangle with the provided brush at the provided thickness. /// - /// The type of the color. /// The image this method extends. /// The color. /// The thickness. /// The shape. /// The . - public static IImageProcessingContext Draw(this IImageProcessingContext source, TPixel color, float thickness, RectangleF shape) - where TPixel : struct, IPixel - => source.Draw(new SolidBrush(color), thickness, shape); + public static IImageProcessingContext Draw( + this IImageProcessingContext source, + Color color, + float thickness, + RectangleF shape) => + source.Draw(new SolidBrush(color), thickness, shape); } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/DrawTextExtensions.cs b/src/ImageSharp.Drawing/Processing/DrawTextExtensions.cs index 46061ce9bc..163a676bbe 100644 --- a/src/ImageSharp.Drawing/Processing/DrawTextExtensions.cs +++ b/src/ImageSharp.Drawing/Processing/DrawTextExtensions.cs @@ -16,7 +16,6 @@ namespace SixLabors.ImageSharp.Processing /// /// Draws the text onto the the image filled via the brush. /// - /// The type of the color. /// The image this method extends. /// The text. /// The font. @@ -25,14 +24,17 @@ namespace SixLabors.ImageSharp.Processing /// /// The . /// - public static IImageProcessingContext DrawText(this IImageProcessingContext source, string text, Font font, TPixel color, PointF location) - where TPixel : struct, IPixel - => source.DrawText(TextGraphicsOptions.Default, text, font, color, location); + public static IImageProcessingContext DrawText( + this IImageProcessingContext source, + string text, + Font font, + Color color, + PointF location) => + source.DrawText(TextGraphicsOptions.Default, text, font, color, location); /// /// Draws the text onto the the image filled via the brush. /// - /// The type of the color. /// The image this method extends. /// The options. /// The text. @@ -42,14 +44,18 @@ namespace SixLabors.ImageSharp.Processing /// /// The . /// - public static IImageProcessingContext DrawText(this IImageProcessingContext source, TextGraphicsOptions options, string text, Font font, TPixel color, PointF location) - where TPixel : struct, IPixel - => source.DrawText(options, text, font, Brushes.Solid(color), null, location); + public static IImageProcessingContext DrawText( + this IImageProcessingContext source, + TextGraphicsOptions options, + string text, + Font font, + Color color, + PointF location) => + source.DrawText(options, text, font, Brushes.Solid(color), null, location); /// /// Draws the text onto the the image filled via the brush. /// - /// The type of the color. /// The image this method extends. /// The text. /// The font. @@ -58,14 +64,17 @@ namespace SixLabors.ImageSharp.Processing /// /// The . /// - public static IImageProcessingContext DrawText(this IImageProcessingContext source, string text, Font font, IBrush brush, PointF location) - where TPixel : struct, IPixel - => source.DrawText(TextGraphicsOptions.Default, text, font, brush, location); + public static IImageProcessingContext DrawText( + this IImageProcessingContext source, + string text, + Font font, + IBrush brush, + PointF location) => + source.DrawText(TextGraphicsOptions.Default, text, font, brush, location); /// /// Draws the text onto the the image filled via the brush. /// - /// The type of the color. /// The image this method extends. /// The options. /// The text. @@ -75,14 +84,18 @@ namespace SixLabors.ImageSharp.Processing /// /// The . /// - public static IImageProcessingContext DrawText(this IImageProcessingContext source, TextGraphicsOptions options, string text, Font font, IBrush brush, PointF location) - where TPixel : struct, IPixel - => source.DrawText(options, text, font, brush, null, location); + public static IImageProcessingContext DrawText( + this IImageProcessingContext source, + TextGraphicsOptions options, + string text, + Font font, + IBrush brush, + PointF location) => + source.DrawText(options, text, font, brush, null, location); /// /// Draws the text onto the the image outlined via the pen. /// - /// The type of the color. /// The image this method extends. /// The text. /// The font. @@ -91,14 +104,17 @@ namespace SixLabors.ImageSharp.Processing /// /// The . /// - public static IImageProcessingContext DrawText(this IImageProcessingContext source, string text, Font font, IPen pen, PointF location) - where TPixel : struct, IPixel - => source.DrawText(TextGraphicsOptions.Default, text, font, pen, location); + public static IImageProcessingContext DrawText( + this IImageProcessingContext source, + string text, + Font font, + IPen pen, + PointF location) => + source.DrawText(TextGraphicsOptions.Default, text, font, pen, location); /// /// Draws the text onto the the image outlined via the pen. /// - /// The type of the color. /// The image this method extends. /// The options. /// The text. @@ -108,14 +124,18 @@ namespace SixLabors.ImageSharp.Processing /// /// The . /// - public static IImageProcessingContext DrawText(this IImageProcessingContext source, TextGraphicsOptions options, string text, Font font, IPen pen, PointF location) - where TPixel : struct, IPixel - => source.DrawText(options, text, font, null, pen, location); + public static IImageProcessingContext DrawText( + this IImageProcessingContext source, + TextGraphicsOptions options, + string text, + Font font, + IPen pen, + PointF location) => + source.DrawText(options, text, font, null, pen, location); /// /// Draws the text onto the the image filled via the brush then outlined via the pen. /// - /// The type of the color. /// The image this method extends. /// The text. /// The font. @@ -125,14 +145,18 @@ namespace SixLabors.ImageSharp.Processing /// /// The . /// - public static IImageProcessingContext DrawText(this IImageProcessingContext source, string text, Font font, IBrush brush, IPen pen, PointF location) - where TPixel : struct, IPixel - => source.DrawText(TextGraphicsOptions.Default, text, font, brush, pen, location); + public static IImageProcessingContext DrawText( + this IImageProcessingContext source, + string text, + Font font, + IBrush brush, + IPen pen, + PointF location) => + source.DrawText(TextGraphicsOptions.Default, text, font, brush, pen, location); /// /// Draws the text using the default resolution of 72dpi onto the the image filled via the brush then outlined via the pen. /// - /// The type of the color. /// The image this method extends. /// The options. /// The text. @@ -143,8 +167,14 @@ namespace SixLabors.ImageSharp.Processing /// /// The . /// - public static IImageProcessingContext DrawText(this IImageProcessingContext source, TextGraphicsOptions options, string text, Font font, IBrush brush, IPen pen, PointF location) - where TPixel : struct, IPixel - => source.ApplyProcessor(new DrawTextProcessor(options, text, font, brush, pen, location)); + public static IImageProcessingContext DrawText( + this IImageProcessingContext source, + TextGraphicsOptions options, + string text, + Font font, + IBrush brush, + IPen pen, + PointF location) => + source.ApplyProcessor(new DrawTextProcessor(options, text, font, brush, pen, location)); } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/EllipticGradientBrush{TPixel}.cs b/src/ImageSharp.Drawing/Processing/EllipticGradientBrush{TPixel}.cs index 64901c8e80..4a8ce44dbb 100644 --- a/src/ImageSharp.Drawing/Processing/EllipticGradientBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/EllipticGradientBrush{TPixel}.cs @@ -14,9 +14,7 @@ namespace SixLabors.ImageSharp.Processing /// a point on the longest extension of the ellipse and /// the ratio between longest and shortest extension. /// - /// The Pixel format that is used. - public sealed class EllipticGradientBrush : GradientBrushBase - where TPixel : struct, IPixel + public sealed class EllipticGradientBrush : GradientBrushBase { private readonly PointF center; @@ -24,7 +22,7 @@ namespace SixLabors.ImageSharp.Processing private readonly float axisRatio; - /// + /// /// The center of the elliptical gradient and 0 for the color stops. /// The end point of the reference axis of the ellipse. /// @@ -39,7 +37,7 @@ namespace SixLabors.ImageSharp.Processing PointF referenceAxisEnd, float axisRatio, GradientRepetitionMode repetitionMode, - params ColorStop[] colorStops) + params ColorStop[] colorStops) : base(repetitionMode, colorStops) { this.center = center; @@ -47,12 +45,12 @@ namespace SixLabors.ImageSharp.Processing this.axisRatio = axisRatio; } - /// - public override BrushApplicator CreateApplicator( + /// + public override BrushApplicator CreateApplicator( ImageFrame source, RectangleF region, GraphicsOptions options) => - new RadialGradientBrushApplicator( + new RadialGradientBrushApplicator( source, options, this.center, @@ -62,7 +60,8 @@ namespace SixLabors.ImageSharp.Processing this.RepetitionMode); /// - private sealed class RadialGradientBrushApplicator : GradientBrushApplicatorBase + private sealed class RadialGradientBrushApplicator : GradientBrushApplicatorBase + where TPixel : struct, IPixel { private readonly PointF center; @@ -85,7 +84,7 @@ namespace SixLabors.ImageSharp.Processing private readonly float referenceRadiusSquared; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The target image /// The options @@ -102,7 +101,7 @@ namespace SixLabors.ImageSharp.Processing PointF center, PointF referenceAxisEnd, float axisRatio, - ColorStop[] colorStops, + ColorStop[] colorStops, GradientRepetitionMode repetitionMode) : base(target, options, colorStops, repetitionMode) { diff --git a/src/ImageSharp.Drawing/Processing/FillPathBuilderExtensions.cs b/src/ImageSharp.Drawing/Processing/FillPathBuilderExtensions.cs index ff4de3ff8f..ca7eab93f9 100644 --- a/src/ImageSharp.Drawing/Processing/FillPathBuilderExtensions.cs +++ b/src/ImageSharp.Drawing/Processing/FillPathBuilderExtensions.cs @@ -15,14 +15,16 @@ namespace SixLabors.ImageSharp.Processing /// /// Flood fills the image in the shape of the provided polygon with the specified brush. /// - /// The type of the color. /// The image this method extends. /// The graphics options. /// The brush. /// The shape. /// The . - public static IImageProcessingContext Fill(this IImageProcessingContext source, GraphicsOptions options, IBrush brush, Action path) - where TPixel : struct, IPixel + public static IImageProcessingContext Fill( + this IImageProcessingContext source, + GraphicsOptions options, + IBrush brush, + Action path) { var pb = new PathBuilder(); path(pb); @@ -33,38 +35,42 @@ namespace SixLabors.ImageSharp.Processing /// /// Flood fills the image in the shape of the provided polygon with the specified brush. /// - /// The type of the color. /// The image this method extends. /// The brush. /// The path. /// The . - public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, Action path) - where TPixel : struct, IPixel - => source.Fill(GraphicsOptions.Default, brush, path); + public static IImageProcessingContext Fill( + this IImageProcessingContext source, + IBrush brush, + Action path) => + source.Fill(GraphicsOptions.Default, brush, path); /// /// Flood fills the image in the shape of the provided polygon with the specified brush. /// - /// The type of the color. /// The image this method extends. /// The options. /// The color. /// The path. /// The . - public static IImageProcessingContext Fill(this IImageProcessingContext source, GraphicsOptions options, TPixel color, Action path) - where TPixel : struct, IPixel - => source.Fill(options, new SolidBrush(color), path); + public static IImageProcessingContext Fill( + this IImageProcessingContext source, + GraphicsOptions options, + Color color, + Action path) => + source.Fill(options, new SolidBrush(color), path); /// /// Flood fills the image in the shape of the provided polygon with the specified brush. /// - /// The type of the color. /// The image this method extends. /// The color. /// The path. /// The . - public static IImageProcessingContext Fill(this IImageProcessingContext source, TPixel color, Action path) - where TPixel : struct, IPixel - => source.Fill(new SolidBrush(color), path); + public static IImageProcessingContext Fill( + this IImageProcessingContext source, + Color color, + Action path) => + source.Fill(new SolidBrush(color), path); } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/FillPathCollectionExtensions.cs b/src/ImageSharp.Drawing/Processing/FillPathCollectionExtensions.cs index da2dd35b6a..d750ff455b 100644 --- a/src/ImageSharp.Drawing/Processing/FillPathCollectionExtensions.cs +++ b/src/ImageSharp.Drawing/Processing/FillPathCollectionExtensions.cs @@ -14,14 +14,16 @@ namespace SixLabors.ImageSharp.Processing /// /// Flood fills the image in the shape of the provided polygon with the specified brush. /// - /// The type of the color. /// The image this method extends. /// The graphics options. /// The brush. /// The shapes. /// The . - public static IImageProcessingContext Fill(this IImageProcessingContext source, GraphicsOptions options, IBrush brush, IPathCollection paths) - where TPixel : struct, IPixel + public static IImageProcessingContext Fill( + this IImageProcessingContext source, + GraphicsOptions options, + IBrush brush, + IPathCollection paths) { foreach (IPath s in paths) { @@ -34,38 +36,42 @@ namespace SixLabors.ImageSharp.Processing /// /// Flood fills the image in the shape of the provided polygon with the specified brush. /// - /// The type of the color. /// The image this method extends. /// The brush. /// The paths. /// The . - public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, IPathCollection paths) - where TPixel : struct, IPixel - => source.Fill(GraphicsOptions.Default, brush, paths); + public static IImageProcessingContext Fill( + this IImageProcessingContext source, + IBrush brush, + IPathCollection paths) => + source.Fill(GraphicsOptions.Default, brush, paths); /// /// Flood fills the image in the shape of the provided polygon with the specified brush. /// - /// The type of the color. /// The image this method extends. /// The options. /// The color. /// The paths. /// The . - public static IImageProcessingContext Fill(this IImageProcessingContext source, GraphicsOptions options, TPixel color, IPathCollection paths) - where TPixel : struct, IPixel - => source.Fill(options, new SolidBrush(color), paths); + public static IImageProcessingContext Fill( + this IImageProcessingContext source, + GraphicsOptions options, + Color color, + IPathCollection paths) => + source.Fill(options, new SolidBrush(color), paths); /// /// Flood fills the image in the shape of the provided polygon with the specified brush. /// - /// The type of the color. /// The image this method extends. /// The color. /// The paths. /// The . - public static IImageProcessingContext Fill(this IImageProcessingContext source, TPixel color, IPathCollection paths) - where TPixel : struct, IPixel - => source.Fill(new SolidBrush(color), paths); + public static IImageProcessingContext Fill( + this IImageProcessingContext source, + Color color, + IPathCollection paths) => + source.Fill(new SolidBrush(color), paths); } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/FillPathExtensions.cs b/src/ImageSharp.Drawing/Processing/FillPathExtensions.cs index da10621113..7cf67612a2 100644 --- a/src/ImageSharp.Drawing/Processing/FillPathExtensions.cs +++ b/src/ImageSharp.Drawing/Processing/FillPathExtensions.cs @@ -15,51 +15,51 @@ namespace SixLabors.ImageSharp.Processing /// /// Flood fills the image in the shape of the provided polygon with the specified brush. /// - /// The type of the color. + /// The image this method extends. /// The graphics options. /// The brush. /// The shape. /// The . - public static IImageProcessingContext Fill(this IImageProcessingContext source, GraphicsOptions options, IBrush brush, IPath path) - where TPixel : struct, IPixel + public static IImageProcessingContext Fill(this IImageProcessingContext source, GraphicsOptions options, IBrush brush, IPath path) + => source.Fill(options, brush, new ShapeRegion(path)); /// /// Flood fills the image in the shape of the provided polygon with the specified brush. /// - /// The type of the color. + /// The image this method extends. /// The brush. /// The path. /// The . - public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, IPath path) - where TPixel : struct, IPixel + public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, IPath path) + => source.Fill(GraphicsOptions.Default, brush, new ShapeRegion(path)); /// /// Flood fills the image in the shape of the provided polygon with the specified brush.. /// - /// The type of the color. + /// The image this method extends. /// The options. /// The color. /// The path. /// The . - public static IImageProcessingContext Fill(this IImageProcessingContext source, GraphicsOptions options, TPixel color, IPath path) - where TPixel : struct, IPixel - => source.Fill(options, new SolidBrush(color), path); + public static IImageProcessingContext Fill(this IImageProcessingContext source, GraphicsOptions options, Color color, IPath path) + + => source.Fill(options, new SolidBrush(color), path); /// /// Flood fills the image in the shape of the provided polygon with the specified brush.. /// - /// The type of the color. + /// The image this method extends. /// The color. /// The path. /// The . - public static IImageProcessingContext Fill(this IImageProcessingContext source, TPixel color, IPath path) - where TPixel : struct, IPixel - => source.Fill(new SolidBrush(color), path); + public static IImageProcessingContext Fill(this IImageProcessingContext source, Color color, IPath path) + + => source.Fill(new SolidBrush(color), path); } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/FillPolygonExtensions.cs b/src/ImageSharp.Drawing/Processing/FillPolygonExtensions.cs index 970ca22644..8150c7381e 100644 --- a/src/ImageSharp.Drawing/Processing/FillPolygonExtensions.cs +++ b/src/ImageSharp.Drawing/Processing/FillPolygonExtensions.cs @@ -15,51 +15,57 @@ namespace SixLabors.ImageSharp.Processing /// /// Flood fills the image in the shape of a Linear polygon described by the points /// - /// The type of the color. /// The image this method extends. /// The options. /// The brush. /// The points. /// The . - public static IImageProcessingContext FillPolygon(this IImageProcessingContext source, GraphicsOptions options, IBrush brush, params PointF[] points) - where TPixel : struct, IPixel - => source.Fill(options, brush, new Polygon(new LinearLineSegment(points))); + public static IImageProcessingContext FillPolygon( + this IImageProcessingContext source, + GraphicsOptions options, + IBrush brush, + params PointF[] points) => + source.Fill(options, brush, new Polygon(new LinearLineSegment(points))); /// /// Flood fills the image in the shape of a Linear polygon described by the points /// - /// The type of the color. /// The image this method extends. /// The brush. /// The points. /// The . - public static IImageProcessingContext FillPolygon(this IImageProcessingContext source, IBrush brush, params PointF[] points) - where TPixel : struct, IPixel - => source.Fill(brush, new Polygon(new LinearLineSegment(points))); + public static IImageProcessingContext FillPolygon( + this IImageProcessingContext source, + IBrush brush, + params PointF[] points) => + source.Fill(brush, new Polygon(new LinearLineSegment(points))); /// /// Flood fills the image in the shape of a Linear polygon described by the points /// - /// The type of the color. /// The image this method extends. /// The options. /// The color. /// The points. /// The . - public static IImageProcessingContext FillPolygon(this IImageProcessingContext source, GraphicsOptions options, TPixel color, params PointF[] points) - where TPixel : struct, IPixel - => source.Fill(options, new SolidBrush(color), new Polygon(new LinearLineSegment(points))); + public static IImageProcessingContext FillPolygon( + this IImageProcessingContext source, + GraphicsOptions options, + Color color, + params PointF[] points) => + source.Fill(options, new SolidBrush(color), new Polygon(new LinearLineSegment(points))); /// /// Flood fills the image in the shape of a Linear polygon described by the points /// - /// The type of the color. /// The image this method extends. /// The color. /// The points. /// The . - public static IImageProcessingContext FillPolygon(this IImageProcessingContext source, TPixel color, params PointF[] points) - where TPixel : struct, IPixel - => source.Fill(new SolidBrush(color), new Polygon(new LinearLineSegment(points))); + public static IImageProcessingContext FillPolygon( + this IImageProcessingContext source, + Color color, + params PointF[] points) => + source.Fill(new SolidBrush(color), new Polygon(new LinearLineSegment(points))); } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/FillRectangleExtensions.cs b/src/ImageSharp.Drawing/Processing/FillRectangleExtensions.cs index 26bf214f71..ad512015e2 100644 --- a/src/ImageSharp.Drawing/Processing/FillRectangleExtensions.cs +++ b/src/ImageSharp.Drawing/Processing/FillRectangleExtensions.cs @@ -15,51 +15,53 @@ namespace SixLabors.ImageSharp.Processing /// /// Flood fills the image in the shape of the provided rectangle with the specified brush. /// - /// The type of the color. /// The image this method extends. /// The options. /// The brush. /// The shape. /// The . - public static IImageProcessingContext Fill(this IImageProcessingContext source, GraphicsOptions options, IBrush brush, RectangleF shape) - where TPixel : struct, IPixel - => source.Fill(options, brush, new RectangularPolygon(shape.X, shape.Y, shape.Width, shape.Height)); + public static IImageProcessingContext Fill( + this IImageProcessingContext source, + GraphicsOptions options, + IBrush brush, + RectangleF shape) => + source.Fill(options, brush, new RectangularPolygon(shape.X, shape.Y, shape.Width, shape.Height)); /// /// Flood fills the image in the shape of the provided rectangle with the specified brush. /// - /// The type of the color. /// The image this method extends. /// The brush. /// The shape. /// The . - public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, RectangleF shape) - where TPixel : struct, IPixel - => source.Fill(brush, new RectangularPolygon(shape.X, shape.Y, shape.Width, shape.Height)); + public static IImageProcessingContext + Fill(this IImageProcessingContext source, IBrush brush, RectangleF shape) => + source.Fill(brush, new RectangularPolygon(shape.X, shape.Y, shape.Width, shape.Height)); /// /// Flood fills the image in the shape of the provided rectangle with the specified brush. /// - /// The type of the color. /// The image this method extends. /// The options. /// The color. /// The shape. /// The . - public static IImageProcessingContext Fill(this IImageProcessingContext source, GraphicsOptions options, TPixel color, RectangleF shape) - where TPixel : struct, IPixel - => source.Fill(options, new SolidBrush(color), shape); + public static IImageProcessingContext Fill( + this IImageProcessingContext source, + GraphicsOptions options, + Color color, + RectangleF shape) => + source.Fill(options, new SolidBrush(color), shape); /// /// Flood fills the image in the shape of the provided rectangle with the specified brush. /// - /// The type of the color. /// The image this method extends. /// The color. /// The shape. /// The . - public static IImageProcessingContext Fill(this IImageProcessingContext source, TPixel color, RectangleF shape) - where TPixel : struct, IPixel - => source.Fill(new SolidBrush(color), shape); + public static IImageProcessingContext + Fill(this IImageProcessingContext source, Color color, RectangleF shape) => + source.Fill(new SolidBrush(color), shape); } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/FillRegionExtensions.cs b/src/ImageSharp.Drawing/Processing/FillRegionExtensions.cs index e566d03231..457ad98b0c 100644 --- a/src/ImageSharp.Drawing/Processing/FillRegionExtensions.cs +++ b/src/ImageSharp.Drawing/Processing/FillRegionExtensions.cs @@ -15,85 +15,85 @@ namespace SixLabors.ImageSharp.Processing /// /// Flood fills the image with the specified brush. /// - /// The type of the color. + /// The image this method extends. /// The details how to fill the region of interest. /// The . - public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush) - where TPixel : struct, IPixel + public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush) + => source.Fill(GraphicsOptions.Default, brush); /// /// Flood fills the image with the specified color. /// - /// The type of the color. + /// The image this method extends. /// The color. /// The . - public static IImageProcessingContext Fill(this IImageProcessingContext source, TPixel color) - where TPixel : struct, IPixel - => source.Fill(new SolidBrush(color)); + public static IImageProcessingContext Fill(this IImageProcessingContext source, Color color) + + => source.Fill(new SolidBrush(color)); /// /// Flood fills the image with in the region with the specified brush. /// - /// The type of the color. + /// The image this method extends. /// The brush. /// The region. /// The . - public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, Region region) - where TPixel : struct, IPixel + public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, Region region) + => source.Fill(GraphicsOptions.Default, brush, region); /// /// Flood fills the image with in the region with the specified color. /// - /// The type of the color. + /// The image this method extends. /// The options. /// The color. /// The region. /// The . - public static IImageProcessingContext Fill(this IImageProcessingContext source, GraphicsOptions options, TPixel color, Region region) - where TPixel : struct, IPixel - => source.Fill(options, new SolidBrush(color), region); + public static IImageProcessingContext Fill(this IImageProcessingContext source, GraphicsOptions options, Color color, Region region) + + => source.Fill(options, new SolidBrush(color), region); /// /// Flood fills the image with in the region with the specified color. /// - /// The type of the color. + /// The image this method extends. /// The color. /// The region. /// The . - public static IImageProcessingContext Fill(this IImageProcessingContext source, TPixel color, Region region) - where TPixel : struct, IPixel - => source.Fill(new SolidBrush(color), region); + public static IImageProcessingContext Fill(this IImageProcessingContext source, Color color, Region region) + + => source.Fill(new SolidBrush(color), region); /// /// Flood fills the image with in the region with the specified brush. /// - /// The type of the color. + /// The image this method extends. /// The graphics options. /// The brush. /// The region. /// The . - public static IImageProcessingContext Fill(this IImageProcessingContext source, GraphicsOptions options, IBrush brush, Region region) - where TPixel : struct, IPixel - => source.ApplyProcessor(new FillRegionProcessor(brush, region, options)); + public static IImageProcessingContext Fill(this IImageProcessingContext source, GraphicsOptions options, IBrush brush, Region region) + + => source.ApplyProcessor(new FillRegionProcessor(brush, region, options)); /// /// Flood fills the image with the specified brush. /// - /// The type of the color. + /// The image this method extends. /// The graphics options. /// The details how to fill the region of interest. /// The . - public static IImageProcessingContext Fill(this IImageProcessingContext source, GraphicsOptions options, IBrush brush) - where TPixel : struct, IPixel - => source.ApplyProcessor(new FillProcessor(brush, options)); + public static IImageProcessingContext Fill(this IImageProcessingContext source, GraphicsOptions options, IBrush brush) + + => source.ApplyProcessor(new FillProcessor(brush, options)); } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/GradientBrushBase{TPixel}.cs b/src/ImageSharp.Drawing/Processing/GradientBrushBase{TPixel}.cs index 2dfae9e8f1..a877ea96cd 100644 --- a/src/ImageSharp.Drawing/Processing/GradientBrushBase{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/GradientBrushBase{TPixel}.cs @@ -13,16 +13,14 @@ namespace SixLabors.ImageSharp.Processing /// /// Base class for Gradient brushes /// - /// The pixel format - public abstract class GradientBrushBase : IBrush - where TPixel : struct, IPixel + public abstract class GradientBrushBase : IBrush { - /// + /// /// Defines how the colors are repeated beyond the interval [0..1] /// The gradient colors. protected GradientBrushBase( GradientRepetitionMode repetitionMode, - params ColorStop[] colorStops) + params ColorStop[] colorStops) { this.RepetitionMode = repetitionMode; this.ColorStops = colorStops; @@ -36,25 +34,27 @@ namespace SixLabors.ImageSharp.Processing /// /// Gets the list of color stops for this gradient. /// - protected ColorStop[] ColorStops { get; } + protected ColorStop[] ColorStops { get; } - /// - public abstract BrushApplicator CreateApplicator( + /// + public abstract BrushApplicator CreateApplicator( ImageFrame source, RectangleF region, - GraphicsOptions options); + GraphicsOptions options) + where TPixel : struct, IPixel; /// /// Base class for gradient brush applicators /// - protected abstract class GradientBrushApplicatorBase : BrushApplicator + protected abstract class GradientBrushApplicatorBase : BrushApplicator + where TPixel : struct, IPixel { - private readonly ColorStop[] colorStops; + private readonly ColorStop[] colorStops; private readonly GradientRepetitionMode repetitionMode; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The target. /// The options. @@ -63,7 +63,7 @@ namespace SixLabors.ImageSharp.Processing protected GradientBrushApplicatorBase( ImageFrame target, GraphicsOptions options, - ColorStop[] colorStops, + ColorStop[] colorStops, GradientRepetitionMode repetitionMode) : base(target, options) { @@ -111,11 +111,11 @@ namespace SixLabors.ImageSharp.Processing throw new ArgumentOutOfRangeException(); } - (ColorStop from, ColorStop to) = this.GetGradientSegment(positionOnCompleteGradient); + (ColorStop from, ColorStop to) = this.GetGradientSegment(positionOnCompleteGradient); if (from.Color.Equals(to.Color)) { - return from.Color; + return from.Color.ToPixel(); } else { @@ -150,14 +150,14 @@ namespace SixLabors.ImageSharp.Processing /// protected abstract float PositionOnGradient(float x, float y); - private (ColorStop from, ColorStop to) GetGradientSegment( + private (ColorStop from, ColorStop to) GetGradientSegment( float positionOnCompleteGradient) { - ColorStop localGradientFrom = this.colorStops[0]; - ColorStop localGradientTo = default; + ColorStop localGradientFrom = this.colorStops[0]; + ColorStop localGradientTo = default; // TODO: ensure colorStops has at least 2 items (technically 1 would be okay, but that's no gradient) - foreach (ColorStop colorStop in this.colorStops) + foreach (ColorStop colorStop in this.colorStops) { localGradientTo = colorStop; diff --git a/src/ImageSharp.Drawing/Processing/LinearGradientBrush{TPixel}.cs b/src/ImageSharp.Drawing/Processing/LinearGradientBrush{TPixel}.cs index efdc852f28..1a6f8d79ab 100644 --- a/src/ImageSharp.Drawing/Processing/LinearGradientBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/LinearGradientBrush{TPixel}.cs @@ -14,15 +14,15 @@ namespace SixLabors.ImageSharp.Processing /// - a set of colors in relative distances to each other. /// /// The pixel format - public sealed class LinearGradientBrush : GradientBrushBase - where TPixel : struct, IPixel + public sealed class LinearGradientBrush : GradientBrushBase + { private readonly PointF p1; private readonly PointF p2; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Start point /// End point @@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Processing PointF p1, PointF p2, GradientRepetitionMode repetitionMode, - params ColorStop[] colorStops) + params ColorStop[] colorStops) : base(repetitionMode, colorStops) { this.p1 = p1; @@ -40,13 +40,14 @@ namespace SixLabors.ImageSharp.Processing } /// - public override BrushApplicator CreateApplicator(ImageFrame source, RectangleF region, GraphicsOptions options) - => new LinearGradientBrushApplicator(source, this.p1, this.p2, this.ColorStops, this.RepetitionMode, options); + public override BrushApplicator CreateApplicator(ImageFrame source, RectangleF region, GraphicsOptions options) + => new LinearGradientBrushApplicator(source, this.p1, this.p2, this.ColorStops, this.RepetitionMode, options); /// /// The linear gradient brush applicator. /// - private sealed class LinearGradientBrushApplicator : GradientBrushApplicatorBase + private sealed class LinearGradientBrushApplicator : GradientBrushApplicatorBase + where TPixel : struct, IPixel { private readonly PointF start; @@ -83,7 +84,7 @@ namespace SixLabors.ImageSharp.Processing private readonly float length; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The source /// start point of the gradient @@ -95,7 +96,7 @@ namespace SixLabors.ImageSharp.Processing ImageFrame source, PointF start, PointF end, - ColorStop[] colorStops, + ColorStop[] colorStops, GradientRepetitionMode repetitionMode, GraphicsOptions options) : base(source, options, colorStops, repetitionMode) diff --git a/src/ImageSharp.Drawing/Processing/PatternBrush{TPixel}.cs b/src/ImageSharp.Drawing/Processing/PatternBrush{TPixel}.cs index 0e66756721..21690055e1 100644 --- a/src/ImageSharp.Drawing/Processing/PatternBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/PatternBrush{TPixel}.cs @@ -34,7 +34,6 @@ namespace SixLabors.ImageSharp.Processing /// 0 /// /// - /// The pixel format. public class PatternBrush : IBrush { /// @@ -44,7 +43,7 @@ namespace SixLabors.ImageSharp.Processing private readonly DenseMatrix patternVector; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Color of the fore. /// Color of the back. @@ -55,7 +54,7 @@ namespace SixLabors.ImageSharp.Processing } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Color of the fore. /// Color of the back. @@ -85,7 +84,7 @@ namespace SixLabors.ImageSharp.Processing /// Initializes a new instance of the class. /// /// The brush. - internal PatternBrush(PatternBrush brush) + internal PatternBrush(PatternBrush brush) { this.pattern = brush.pattern; this.patternVector = brush.patternVector; diff --git a/src/ImageSharp.Drawing/Processing/RadialGradientBrush{TPixel}.cs b/src/ImageSharp.Drawing/Processing/RadialGradientBrush{TPixel}.cs index d33d099931..84a97e7d1f 100644 --- a/src/ImageSharp.Drawing/Processing/RadialGradientBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/RadialGradientBrush{TPixel}.cs @@ -11,15 +11,14 @@ namespace SixLabors.ImageSharp.Processing /// /// A Circular Gradient Brush, defined by center point and radius. /// - /// The pixel format. - public sealed class RadialGradientBrush : GradientBrushBase - where TPixel : struct, IPixel + public sealed class RadialGradientBrush : GradientBrushBase + { private readonly PointF center; private readonly float radius; - /// + /// /// The center of the circular gradient and 0 for the color stops. /// The radius of the circular gradient and 1 for the color stops. /// Defines how the colors in the gradient are repeated. @@ -28,19 +27,19 @@ namespace SixLabors.ImageSharp.Processing PointF center, float radius, GradientRepetitionMode repetitionMode, - params ColorStop[] colorStops) + params ColorStop[] colorStops) : base(repetitionMode, colorStops) { this.center = center; this.radius = radius; } - /// - public override BrushApplicator CreateApplicator( + /// + public override BrushApplicator CreateApplicator( ImageFrame source, RectangleF region, GraphicsOptions options) => - new RadialGradientBrushApplicator( + new RadialGradientBrushApplicator( source, options, this.center, @@ -49,14 +48,15 @@ namespace SixLabors.ImageSharp.Processing this.RepetitionMode); /// - private sealed class RadialGradientBrushApplicator : GradientBrushApplicatorBase + private sealed class RadialGradientBrushApplicator : GradientBrushApplicatorBase + where TPixel : struct, IPixel { private readonly PointF center; private readonly float radius; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The target image /// The options. @@ -69,7 +69,7 @@ namespace SixLabors.ImageSharp.Processing GraphicsOptions options, PointF center, float radius, - ColorStop[] colorStops, + ColorStop[] colorStops, GradientRepetitionMode repetitionMode) : base(target, options, colorStops, repetitionMode) { diff --git a/src/ImageSharp/GraphicsOptions.cs b/src/ImageSharp/GraphicsOptions.cs index 537418de24..4d5bf6d513 100644 --- a/src/ImageSharp/GraphicsOptions.cs +++ b/src/ImageSharp/GraphicsOptions.cs @@ -141,7 +141,6 @@ namespace SixLabors.ImageSharp /// /// Evaluates if a given SOURCE color can completely replace a BACKDROP color given the current blending and composition settings. /// - /// The pixel format /// the color /// true if the color can be considered opaque /// From d3af717d1971bbb14c71cfdd35920ce340d0eb8c Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 12 May 2019 21:52:33 +0200 Subject: [PATCH 181/223] everything builds --- .../Drawing/DrawBeziers.cs | 2 +- .../Drawing/DrawLines.cs | 2 +- .../Drawing/DrawPolygon.cs | 2 +- .../ImageSharp.Benchmarks/Drawing/DrawText.cs | 4 +- .../Drawing/DrawTextOutline.cs | 23 +++- .../Drawing/DrawBezierTests.cs | 2 +- .../Drawing/DrawComplexPolygonTests.cs | 2 +- .../Drawing/DrawLinesTests.cs | 12 +- .../ImageSharp.Tests/Drawing/DrawPathTests.cs | 4 +- .../Drawing/DrawPolygonTests.cs | 2 +- .../Drawing/FillComplexPolygonTests.cs | 2 +- .../Drawing/FillEllipticGradientBrushTest.cs | 36 +++--- .../Drawing/FillLinearGradientBrushTests.cs | 104 +++++++++--------- .../Drawing/FillPatternBrushTests.cs | 2 +- .../Drawing/FillPolygonTests.cs | 12 +- .../Drawing/FillRadialGradientBrushTests.cs | 14 +-- .../Drawing/FillRegionProcessorTests.cs | 11 +- .../Drawing/FillSolidBrushTests.cs | 22 ++-- .../Drawing/Paths/DrawPathCollection.cs | 16 +-- .../Drawing/Paths/FillPath.cs | 16 +-- .../Drawing/Paths/FillPathCollection.cs | 16 +-- .../Drawing/Paths/FillPolygon.cs | 16 +-- .../Drawing/Paths/FillRectangle.cs | 16 +-- .../Drawing/RecolorImageTests.cs | 4 +- .../Drawing/SolidBezierTests.cs | 2 +- .../Drawing/SolidFillBlendedShapesTests.cs | 26 ++--- .../ImageSharp.Tests/Drawing/Text/DrawText.cs | 70 ++++++------ .../Drawing/Text/DrawTextOnImageTests.cs | 12 +- tests/ImageSharp.Tests/Issues/Issue412.cs | 6 +- .../ImageProviders/SolidProvider.cs | 3 +- .../TestUtilities/TestImageExtensions.cs | 14 +++ 31 files changed, 248 insertions(+), 227 deletions(-) diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs index 0a343864d8..8999afaeda 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs @@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Benchmarks graphics.InterpolationMode = InterpolationMode.Default; graphics.SmoothingMode = SmoothingMode.AntiAlias; - using (var pen = new Pen(System.Drawing.Color.HotPink, 10)) + using (var pen = new System.Drawing.Pen(System.Drawing.Color.HotPink, 10)) { graphics.DrawBeziers(pen, new[] { new PointF(10, 500), diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs index 00942cc364..408e87f9ee 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Benchmarks graphics.InterpolationMode = InterpolationMode.Default; graphics.SmoothingMode = SmoothingMode.AntiAlias; - using (var pen = new Pen(System.Drawing.Color.HotPink, 10)) + using (var pen = new System.Drawing.Pen(System.Drawing.Color.HotPink, 10)) { graphics.DrawLines(pen, new[] { new PointF(10, 10), diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs index dcaf4bae60..2b4cff8cbe 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs @@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Benchmarks { graphics.InterpolationMode = InterpolationMode.Default; graphics.SmoothingMode = SmoothingMode.AntiAlias; - using (var pen = new Pen(System.Drawing.Color.HotPink, 10)) + using (var pen = new System.Drawing.Pen(System.Drawing.Color.HotPink, 10)) { graphics.DrawPolygon(pen, new[] { new PointF(10, 10), diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawText.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawText.cs index 8c840d8c31..28f410e7d1 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/DrawText.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/DrawText.cs @@ -42,7 +42,7 @@ namespace SixLabors.ImageSharp.Benchmarks using (var image = new Image(800, 800)) { var font = SixLabors.Fonts.SystemFonts.CreateFont("Arial", 12); - image.Mutate(x => x.ApplyProcessor(new DrawTextProcessor(new TextGraphicsOptions(true) { WrapTextWidth = 780 }, TextToRender, font, Processing.Brushes.Solid(Rgba32.HotPink), null, new SixLabors.Primitives.PointF(10, 10)))); + image.Mutate(x => x.ApplyProcessor(new DrawTextProcessor(new TextGraphicsOptions(true) { WrapTextWidth = 780 }, TextToRender, font, Processing.Brushes.Solid(Rgba32.HotPink), null, new SixLabors.Primitives.PointF(10, 10)))); } } @@ -55,7 +55,7 @@ namespace SixLabors.ImageSharp.Benchmarks image.Mutate(x => DrawTextOldVersion(x, new TextGraphicsOptions(true) { WrapTextWidth = 780 }, TextToRender, font, Processing.Brushes.Solid(Rgba32.HotPink), null, new SixLabors.Primitives.PointF(10, 10))); } - IImageProcessingContext DrawTextOldVersion(IImageProcessingContext source, TextGraphicsOptions options, string text, SixLabors.Fonts.Font font, IBrush brush, IPen pen, SixLabors.Primitives.PointF location) + IImageProcessingContext DrawTextOldVersion(IImageProcessingContext source, TextGraphicsOptions options, string text, SixLabors.Fonts.Font font, IBrush brush, IPen pen, SixLabors.Primitives.PointF location) where TPixel : struct, IPixel { float dpiX = 72; diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawTextOutline.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawTextOutline.cs index ee17adbb55..658590e2d4 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/DrawTextOutline.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/DrawTextOutline.cs @@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Benchmarks { graphics.InterpolationMode = InterpolationMode.Default; graphics.SmoothingMode = SmoothingMode.AntiAlias; - using (var pen = new Pen(System.Drawing.Color.HotPink, 10)) + using (var pen = new System.Drawing.Pen(System.Drawing.Color.HotPink, 10)) using (var font = new Font("Arial", 12, GraphicsUnit.Point)) using (var gp = new GraphicsPath()) { @@ -43,7 +43,7 @@ namespace SixLabors.ImageSharp.Benchmarks using (var image = new Image(800, 800)) { var font = SixLabors.Fonts.SystemFonts.CreateFont("Arial", 12); - image.Mutate(x => x.ApplyProcessor(new DrawTextProcessor(new TextGraphicsOptions(true) { WrapTextWidth = 780 }, TextToRender, font, null, Processing.Pens.Solid(Rgba32.HotPink, 10), new SixLabors.Primitives.PointF(10, 10)))); + image.Mutate(x => x.ApplyProcessor(new DrawTextProcessor(new TextGraphicsOptions(true) { WrapTextWidth = 780 }, TextToRender, font, null, Processing.Pens.Solid(Rgba32.HotPink, 10), new SixLabors.Primitives.PointF(10, 10)))); } } @@ -53,10 +53,25 @@ namespace SixLabors.ImageSharp.Benchmarks using (var image = new Image(800, 800)) { var font = SixLabors.Fonts.SystemFonts.CreateFont("Arial", 12); - image.Mutate(x => DrawTextOldVersion(x, new TextGraphicsOptions(true) { WrapTextWidth = 780 }, TextToRender, font, null, Processing.Pens.Solid(Rgba32.HotPink, 10), new SixLabors.Primitives.PointF(10, 10))); + image.Mutate( + x => DrawTextOldVersion( + x, + new TextGraphicsOptions(true) { WrapTextWidth = 780 }, + TextToRender, + font, + null, + Processing.Pens.Solid(Rgba32.HotPink, 10), + new SixLabors.Primitives.PointF(10, 10))); } - IImageProcessingContext DrawTextOldVersion(IImageProcessingContext source, TextGraphicsOptions options, string text, SixLabors.Fonts.Font font, IBrush brush, IPen pen, SixLabors.Primitives.PointF location) + IImageProcessingContext DrawTextOldVersion( + IImageProcessingContext source, + TextGraphicsOptions options, + string text, + SixLabors.Fonts.Font font, + IBrush brush, + IPen pen, + SixLabors.Primitives.PointF location) where TPixel : struct, IPixel { var style = new SixLabors.Fonts.RendererOptions(font, options.DpiX, options.DpiY, location) diff --git a/tests/ImageSharp.Tests/Drawing/DrawBezierTests.cs b/tests/ImageSharp.Tests/Drawing/DrawBezierTests.cs index 0f29728c9f..4ccc7fd355 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawBezierTests.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawBezierTests.cs @@ -39,7 +39,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing FormattableString testDetails = $"{colorName}_A{alpha}_T{thickness}"; - provider.RunValidatingProcessorTest( x => x.DrawBeziers(color.ToPixel(), 5f, points), + provider.RunValidatingProcessorTest( x => x.DrawBeziers(color, 5f, points), testDetails, appendSourceFileOrDescription: false, appendPixelTypeToFileName: false); diff --git a/tests/ImageSharp.Tests/Drawing/DrawComplexPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/DrawComplexPolygonTests.cs index b6e105a199..f28f3345fc 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawComplexPolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawComplexPolygonTests.cs @@ -58,7 +58,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing testDetails += "_Dashed"; } - Pen pen = dashed ? Pens.Dash(color.ToPixel(), 5f) : Pens.Solid(color.ToPixel(), 5f); + Pen pen = dashed ? Pens.Dash(color, 5f) : Pens.Solid(color, 5f); provider.RunValidatingProcessorTest( x => x.Draw(pen, clipped), diff --git a/tests/ImageSharp.Tests/Drawing/DrawLinesTests.cs b/tests/ImageSharp.Tests/Drawing/DrawLinesTests.cs index c225b921e6..12eb150b1b 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawLinesTests.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawLinesTests.cs @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing where TPixel : struct, IPixel { Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha); - Pen pen = new Pen(color.ToPixel(), thickness); + Pen pen = new Pen(color, thickness); DrawLinesImpl(provider, colorName, alpha, thickness, antialias, pen); } @@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing where TPixel : struct, IPixel { Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha); - Pen pen = Pens.Dash(color.ToPixel(), thickness); + Pen pen = Pens.Dash(color, thickness); DrawLinesImpl(provider, colorName, alpha, thickness, antialias, pen); } @@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing where TPixel : struct, IPixel { Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha); - Pen pen = Pens.Dot(color.ToPixel(), thickness); + Pen pen = Pens.Dot(color, thickness); DrawLinesImpl(provider, colorName, alpha, thickness, antialias, pen); } @@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing where TPixel : struct, IPixel { Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha); - Pen pen = Pens.DashDot(color.ToPixel(), thickness); + Pen pen = Pens.DashDot(color, thickness); DrawLinesImpl(provider, colorName, alpha, thickness, antialias, pen); } @@ -68,7 +68,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing where TPixel : struct, IPixel { Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha); - Pen pen = Pens.DashDotDot(color.ToPixel(), thickness); + Pen pen = Pens.DashDotDot(color, thickness); DrawLinesImpl(provider, colorName, alpha, thickness, antialias, pen); } @@ -80,7 +80,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing float alpha, float thickness, bool antialias, - Pen pen) + Pen pen) where TPixel : struct, IPixel { SixLabors.Primitives.PointF[] simplePath = { new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300) }; diff --git a/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs b/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs index c638107b81..5e9743c31a 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs @@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing FormattableString testDetails = $"{colorName}_A{alpha}_T{thickness}"; provider.RunValidatingProcessorTest( - x => x.Draw(color.ToPixel(), thickness, path), + x => x.Draw(color, thickness, path), testDetails, appendPixelTypeToFileName: false, appendSourceFileOrDescription: false); @@ -61,7 +61,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing where TPixel : struct, IPixel { Color color = Color.White; - Pen pen = Pens.Solid(color.ToPixel(), 5f); + Pen pen = Pens.Solid(color, 5f); provider.RunValidatingProcessorTest( x => diff --git a/tests/ImageSharp.Tests/Drawing/DrawPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/DrawPolygonTests.cs index 8a0f256670..b7c54fdbd1 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawPolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawPolygonTests.cs @@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing FormattableString outputDetails = $"{colorName}_A({alpha})_T({thickness}){aa}"; provider.RunValidatingProcessorTest( - c => c.DrawPolygon(options, color.ToPixel(), thickness, simplePath), + c => c.DrawPolygon(options, color, thickness, simplePath), outputDetails, appendSourceFileOrDescription: false); } diff --git a/tests/ImageSharp.Tests/Drawing/FillComplexPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/FillComplexPolygonTests.cs index 3887cb9a7a..d6c85cd1d6 100644 --- a/tests/ImageSharp.Tests/Drawing/FillComplexPolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/FillComplexPolygonTests.cs @@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing } provider.RunValidatingProcessorTest( - x => x.Fill(color.ToPixel(), clipped), + x => x.Fill(color, clipped), testDetails, appendPixelTypeToFileName: false, appendSourceFileOrDescription: false); diff --git a/tests/ImageSharp.Tests/Drawing/FillEllipticGradientBrushTest.cs b/tests/ImageSharp.Tests/Drawing/FillEllipticGradientBrushTest.cs index fa4d4a709f..c61f770c9f 100644 --- a/tests/ImageSharp.Tests/Drawing/FillEllipticGradientBrushTest.cs +++ b/tests/ImageSharp.Tests/Drawing/FillEllipticGradientBrushTest.cs @@ -23,18 +23,18 @@ namespace SixLabors.ImageSharp.Tests.Drawing TestImageProvider provider) where TPixel : struct, IPixel { - TPixel red = NamedColors.Red; + Color red = Color.Red; using (Image image = provider.GetImage()) { var unicolorLinearGradientBrush = - new EllipticGradientBrush( + new EllipticGradientBrush( new SixLabors.Primitives.Point(0, 0), new SixLabors.Primitives.Point(10, 0), 1.0f, GradientRepetitionMode.None, - new ColorStop(0, red), - new ColorStop(1, red)); + new ColorStop(0, red), + new ColorStop(1, red)); image.Mutate(x => x.Fill(unicolorLinearGradientBrush)); @@ -58,22 +58,22 @@ namespace SixLabors.ImageSharp.Tests.Drawing float ratio) where TPixel : struct, IPixel { - TPixel yellow = NamedColors.Yellow; - TPixel red = NamedColors.Red; - TPixel black = NamedColors.Black; + Color yellow = Color.Yellow; + Color red = Color.Red; + Color black = Color.Black; provider.VerifyOperation( TolerantComparer, image => { - var unicolorLinearGradientBrush = new EllipticGradientBrush( + var unicolorLinearGradientBrush = new EllipticGradientBrush( new SixLabors.Primitives.Point(image.Width / 2, image.Height / 2), new SixLabors.Primitives.Point(image.Width / 2, (image.Width * 2) / 3), ratio, GradientRepetitionMode.None, - new ColorStop(0, yellow), - new ColorStop(1, red), - new ColorStop(1, black)); + new ColorStop(0, yellow), + new ColorStop(1, red), + new ColorStop(1, black)); image.Mutate(x => x.Fill(unicolorLinearGradientBrush)); }, @@ -114,9 +114,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing TolerantComparer, image => { - TPixel yellow = NamedColors.Yellow; - TPixel red = NamedColors.Red; - TPixel black = NamedColors.Black; + Color yellow = Color.Yellow; + Color red = Color.Red; + Color black = Color.Black; var center = new SixLabors.Primitives.Point(image.Width / 2, image.Height / 2); @@ -128,14 +128,14 @@ namespace SixLabors.ImageSharp.Tests.Drawing int axisX = center.X + (int)-(offsetY * sin); int axisY = center.Y + (int)(offsetY * cos); - var unicolorLinearGradientBrush = new EllipticGradientBrush( + var unicolorLinearGradientBrush = new EllipticGradientBrush( center, new SixLabors.Primitives.Point(axisX, axisY), ratio, GradientRepetitionMode.None, - new ColorStop(0, yellow), - new ColorStop(1, red), - new ColorStop(1, black)); + new ColorStop(0, yellow), + new ColorStop(1, red), + new ColorStop(1, black)); image.Mutate(x => x.Fill(unicolorLinearGradientBrush)); }, diff --git a/tests/ImageSharp.Tests/Drawing/FillLinearGradientBrushTests.cs b/tests/ImageSharp.Tests/Drawing/FillLinearGradientBrushTests.cs index 0f4a98a24a..5d1442e79c 100644 --- a/tests/ImageSharp.Tests/Drawing/FillLinearGradientBrushTests.cs +++ b/tests/ImageSharp.Tests/Drawing/FillLinearGradientBrushTests.cs @@ -29,14 +29,14 @@ namespace SixLabors.ImageSharp.Tests.Drawing { using (Image image = provider.GetImage()) { - TPixel red = NamedColors.Red; + Color red = Color.Red; - var unicolorLinearGradientBrush = new LinearGradientBrush( + var unicolorLinearGradientBrush = new LinearGradientBrush( new SixLabors.Primitives.Point(0, 0), new SixLabors.Primitives.Point(10, 0), GradientRepetitionMode.None, - new ColorStop(0, red), - new ColorStop(1, red)); + new ColorStop(0, red), + new ColorStop(1, red)); image.Mutate(x => x.Fill(unicolorLinearGradientBrush)); @@ -58,12 +58,12 @@ namespace SixLabors.ImageSharp.Tests.Drawing TolerantComparer, image => { - var unicolorLinearGradientBrush = new LinearGradientBrush( + var unicolorLinearGradientBrush = new LinearGradientBrush( new SixLabors.Primitives.Point(0, 0), new SixLabors.Primitives.Point(image.Width, 0), GradientRepetitionMode.None, - new ColorStop(0, NamedColors.Blue), - new ColorStop(1, NamedColors.Yellow)); + new ColorStop(0, Color.Blue), + new ColorStop(1, Color.Yellow)); image.Mutate(x => x.Fill(unicolorLinearGradientBrush)); }, @@ -79,15 +79,15 @@ namespace SixLabors.ImageSharp.Tests.Drawing TolerantComparer, image => { - TPixel red = NamedColors.Red; - TPixel yellow = NamedColors.Yellow; + Color red = Color.Red; + Color yellow = Color.Yellow; - var unicolorLinearGradientBrush = new LinearGradientBrush( + var unicolorLinearGradientBrush = new LinearGradientBrush( new SixLabors.Primitives.Point(0, 0), new SixLabors.Primitives.Point(image.Width, 0), GradientRepetitionMode.None, - new ColorStop(0, red), - new ColorStop(1, yellow)); + new ColorStop(0, red), + new ColorStop(1, yellow)); image.Mutate(x => x.Fill(unicolorLinearGradientBrush)); }, @@ -109,15 +109,15 @@ namespace SixLabors.ImageSharp.Tests.Drawing TolerantComparer, image => { - TPixel red = NamedColors.Red; - TPixel yellow = NamedColors.Yellow; + Color red = Color.Red; + Color yellow = Color.Yellow; - var unicolorLinearGradientBrush = new LinearGradientBrush( + var unicolorLinearGradientBrush = new LinearGradientBrush( new SixLabors.Primitives.Point(0, 0), new SixLabors.Primitives.Point(image.Width / 10, 0), repetitionMode, - new ColorStop(0, red), - new ColorStop(1, yellow)); + new ColorStop(0, red), + new ColorStop(1, yellow)); image.Mutate(x => x.Fill(unicolorLinearGradientBrush)); }, @@ -140,26 +140,26 @@ namespace SixLabors.ImageSharp.Tests.Drawing // ensure the input data is valid Assert.True(pattern.Length > 0); - TPixel black = NamedColors.Black; - TPixel white = NamedColors.White; + Color black = Color.Black; + Color white = Color.White; // create the input pattern: 0, followed by each of the arguments twice, followed by 1.0 - toggling black and white. - ColorStop[] colorStops = - Enumerable.Repeat(new ColorStop(0, black), 1) + ColorStop[] colorStops = + Enumerable.Repeat(new ColorStop(0, black), 1) .Concat( pattern .SelectMany((f, index) => new[] { - new ColorStop(f, index % 2 == 0 ? black : white), - new ColorStop(f, index % 2 == 0 ? white : black) + new ColorStop(f, index % 2 == 0 ? black : white), + new ColorStop(f, index % 2 == 0 ? white : black) })) - .Concat(Enumerable.Repeat(new ColorStop(1, pattern.Length % 2 == 0 ? black : white), 1)) + .Concat(Enumerable.Repeat(new ColorStop(1, pattern.Length % 2 == 0 ? black : white), 1)) .ToArray(); using (Image image = provider.GetImage()) { var unicolorLinearGradientBrush = - new LinearGradientBrush( + new LinearGradientBrush( new SixLabors.Primitives.Point(0, 0), new SixLabors.Primitives.Point(image.Width, 0), GradientRepetitionMode.None, @@ -196,15 +196,15 @@ namespace SixLabors.ImageSharp.Tests.Drawing provider.VerifyOperation( image => { - TPixel red = NamedColors.Red; - TPixel yellow = NamedColors.Yellow; + Color red = Color.Red; + Color yellow = Color.Yellow; - var unicolorLinearGradientBrush = new LinearGradientBrush( + var unicolorLinearGradientBrush = new LinearGradientBrush( new SixLabors.Primitives.Point(0, 0), new SixLabors.Primitives.Point(0, image.Height), GradientRepetitionMode.None, - new ColorStop(0, red), - new ColorStop(1, yellow)); + new ColorStop(0, red), + new ColorStop(1, yellow)); image.Mutate(x => x.Fill(unicolorLinearGradientBrush)); @@ -254,16 +254,16 @@ namespace SixLabors.ImageSharp.Tests.Drawing int endX = image.Height - startX - 1; int endY = image.Width - startY - 1; - TPixel red = NamedColors.Red; - TPixel yellow = NamedColors.Yellow; + Color red = Color.Red; + Color yellow = Color.Yellow; var unicolorLinearGradientBrush = - new LinearGradientBrush( + new LinearGradientBrush( new SixLabors.Primitives.Point(startX, startY), new SixLabors.Primitives.Point(endX, endY), GradientRepetitionMode.None, - new ColorStop(0, red), - new ColorStop(1, yellow)); + new ColorStop(0, red), + new ColorStop(1, yellow)); image.Mutate(x => x.Fill(unicolorLinearGradientBrush)); image.DebugSave( @@ -310,22 +310,21 @@ namespace SixLabors.ImageSharp.Tests.Drawing int[] stopColorCodes) where TPixel : struct, IPixel { - TPixel[] colors = + Color[] colors = { - NamedColors.Navy, NamedColors.LightGreen, NamedColors.Yellow, - NamedColors.Red + Color.Navy, Color.LightGreen, Color.Yellow, + Color.Red }; var coloringVariant = new StringBuilder(); - var colorStops = new ColorStop[stopPositions.Length]; + var colorStops = new ColorStop[stopPositions.Length]; for (int i = 0; i < stopPositions.Length; i++) { - TPixel color = colors[stopColorCodes[i % colors.Length]]; + Color color = colors[stopColorCodes[i % colors.Length]]; float position = stopPositions[i]; - colorStops[i] = new ColorStop(position, color); - Rgba32 rgba = default; - color.ToRgba32(ref rgba); + colorStops[i] = new ColorStop(position, color); + Rgba32 rgba = color; coloringVariant.AppendFormat(CultureInfo.InvariantCulture, "{0}@{1};", rgba.ToHex(), position); } @@ -334,7 +333,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing provider.VerifyOperation( image => { - var unicolorLinearGradientBrush = new LinearGradientBrush( + var unicolorLinearGradientBrush = new LinearGradientBrush( new SixLabors.Primitives.Point(startX, startY), new SixLabors.Primitives.Point(endX, endY), GradientRepetitionMode.None, @@ -357,22 +356,21 @@ namespace SixLabors.ImageSharp.Tests.Drawing int[] stopColorCodes) where TPixel : struct, IPixel { - TPixel[] colors = + Color[] colors = { - NamedColors.Black, NamedColors.Blue, NamedColors.Red, - NamedColors.White, NamedColors.Lime + Color.Black, Color.Blue, Color.Red, + Color.White, Color.Lime }; var coloringVariant = new StringBuilder(); - var colorStops = new ColorStop[stopPositions.Length]; + var colorStops = new ColorStop[stopPositions.Length]; for (int i = 0; i < stopPositions.Length; i++) { - TPixel color = colors[stopColorCodes[i % colors.Length]]; + Color color = colors[stopColorCodes[i % colors.Length]]; float position = stopPositions[i]; - colorStops[i] = new ColorStop(position, color); - Rgba32 rgba = default; - color.ToRgba32(ref rgba); + colorStops[i] = new ColorStop(position, color); + Rgba32 rgba = color; coloringVariant.AppendFormat(CultureInfo.InvariantCulture, "{0}@{1};", rgba.ToHex(), position); } @@ -381,7 +379,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing provider.VerifyOperation( image => { - var unicolorLinearGradientBrush = new LinearGradientBrush( + var unicolorLinearGradientBrush = new LinearGradientBrush( new SixLabors.Primitives.Point(startX, startY), new SixLabors.Primitives.Point(endX, endY), GradientRepetitionMode.None, diff --git a/tests/ImageSharp.Tests/Drawing/FillPatternBrushTests.cs b/tests/ImageSharp.Tests/Drawing/FillPatternBrushTests.cs index dcc689fe1c..647f285103 100644 --- a/tests/ImageSharp.Tests/Drawing/FillPatternBrushTests.cs +++ b/tests/ImageSharp.Tests/Drawing/FillPatternBrushTests.cs @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing { public class FillPatternBrushTests { - private void Test(string name, Rgba32 background, IBrush brush, Rgba32[,] expectedPattern) + private void Test(string name, Rgba32 background, IBrush brush, Rgba32[,] expectedPattern) { string path = TestEnvironment.CreateOutputDirectory("Drawing", "FillPatternBrushTests"); using (var image = new Image(20, 20)) diff --git a/tests/ImageSharp.Tests/Drawing/FillPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/FillPolygonTests.cs index 43029e6957..f84d2b31c9 100644 --- a/tests/ImageSharp.Tests/Drawing/FillPolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/FillPolygonTests.cs @@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing FormattableString outputDetails = $"{colorName}_A{alpha}{aa}"; provider.RunValidatingProcessorTest( - c => c.FillPolygon(options, color.ToPixel(), simplePath), + c => c.FillPolygon(options, color, simplePath), outputDetails, appendSourceFileOrDescription: false); } @@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing Color color = Color.LightGreen; provider.RunValidatingProcessorTest( - c => c.FillPolygon(color.ToPixel(), points), + c => c.FillPolygon(color, points), appendSourceFileOrDescription: false, appendPixelTypeToFileName: false); } @@ -75,7 +75,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing }; Color color = Color.Yellow; - var brush = Brushes.Horizontal(color.ToPixel()); + var brush = Brushes.Horizontal(color); provider.RunValidatingProcessorTest( c => c.FillPolygon(brush, simplePath), @@ -113,7 +113,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing Color color = Color.White; provider.RunValidatingProcessorTest( - c => c.Fill(color.ToPixel(), polygon), + c => c.Fill(color, polygon), appendSourceFileOrDescription: false); } @@ -132,7 +132,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing FormattableString testOutput = $"V({vertices})_R({radius})_Ang({angleDeg})"; provider.RunValidatingProcessorTest( - c => c.Fill(color.ToPixel(), polygon), + c => c.Fill(color, polygon), testOutput, appendSourceFileOrDescription: false, appendPixelTypeToFileName: false); @@ -147,7 +147,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing Color color = Color.Azure; provider.RunValidatingProcessorTest( - c => c.Fill(color.ToPixel(), polygon), + c => c.Fill(color, polygon), appendSourceFileOrDescription: false, appendPixelTypeToFileName: false); } diff --git a/tests/ImageSharp.Tests/Drawing/FillRadialGradientBrushTests.cs b/tests/ImageSharp.Tests/Drawing/FillRadialGradientBrushTests.cs index 7461347de1..818340dd22 100644 --- a/tests/ImageSharp.Tests/Drawing/FillRadialGradientBrushTests.cs +++ b/tests/ImageSharp.Tests/Drawing/FillRadialGradientBrushTests.cs @@ -21,15 +21,15 @@ namespace SixLabors.ImageSharp.Tests.Drawing { using (Image image = provider.GetImage()) { - TPixel red = NamedColors.Red; + Color red = Color.Red; var unicolorRadialGradientBrush = - new RadialGradientBrush( + new RadialGradientBrush( new SixLabors.Primitives.Point(0, 0), 100, GradientRepetitionMode.None, - new ColorStop(0, red), - new ColorStop(1, red)); + new ColorStop(0, red), + new ColorStop(1, red)); image.Mutate(x => x.Fill(unicolorRadialGradientBrush)); @@ -56,12 +56,12 @@ namespace SixLabors.ImageSharp.Tests.Drawing TolerantComparer, image => { - var brush = new RadialGradientBrush( + var brush = new RadialGradientBrush( new SixLabors.Primitives.Point(centerX, centerY), image.Width / 2f, GradientRepetitionMode.None, - new ColorStop(0, NamedColors.Red), - new ColorStop(1, NamedColors.Yellow)); + new ColorStop(0, Color.Red), + new ColorStop(1, Color.Yellow)); image.Mutate(x => x.Fill(brush)); }, diff --git a/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs b/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs index dc7da35433..bb78054a4d 100644 --- a/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs +++ b/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs @@ -8,6 +8,7 @@ using System; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; using SixLabors.Primitives; using Xunit; using SixLabors.ImageSharp.Processing.Processors.Drawing; @@ -30,14 +31,14 @@ namespace SixLabors.ImageSharp.Tests.Drawing { var bounds = new Rectangle(0, 0, 1, 1); - var brush = new Mock>(); + var brush = new Mock(); var region = new MockRegion2(bounds); var options = new GraphicsOptions(antialias) { AntialiasSubpixelDepth = 1 }; - var processor = new FillRegionProcessor(brush.Object, region, options); + var processor = new FillRegionProcessor(brush.Object, region, options); var img = new Image(1, 1); processor.Apply(img, bounds); @@ -48,9 +49,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing public void FillOffCanvas() { var bounds = new Rectangle(-100, -10, 10, 10); - var brush = new Mock>(); + var brush = new Mock(); var options = new GraphicsOptions(true); - var processor = new FillRegionProcessor(brush.Object, new MockRegion1(), options); + var processor = new FillRegionProcessor(brush.Object, new MockRegion1(), options); var img = new Image(10, 10); processor.Apply(img, bounds); } @@ -61,7 +62,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing using (var img = new Image(10, 10)) { - img.Mutate(x => x.DrawLines(new Pen(Rgba32.Black, 10), new SixLabors.Primitives.PointF[] { + img.Mutate(x => x.DrawLines(new Pen(Rgba32.Black, 10), new SixLabors.Primitives.PointF[] { new Vector2(-10, 5), new Vector2(20, 5), })); diff --git a/tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs b/tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs index 639b3fe81a..358552bf81 100644 --- a/tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs +++ b/tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs @@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing { using (Image image = provider.GetImage()) { - TPixel color = NamedColors.HotPink; + Color color = Color.HotPink; image.Mutate(c => c.Fill(color)); image.DebugSave(provider, appendPixelTypeToFileName: false); @@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing { using (Image image = provider.GetImage()) { - TPixel color = NamedColors.HotPink; + Color color = Color.HotPink; image.Mutate(c => c.Fill(color)); image.DebugSave(provider, appendSourceFileOrDescription: false); @@ -63,7 +63,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing { using (Image image = provider.GetImage()) { - TPixel color = TestUtils.GetPixelOfNamedColor(newColorName); + Color color = TestUtils.GetColorByName(newColorName); image.Mutate(c => c.Fill(color)); image.DebugSave( @@ -83,7 +83,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing { FormattableString testDetails = $"(x{x0},y{y0},w{w},h{h})"; var region = new RectangleF(x0, y0, w, h); - TPixel color = TestUtils.GetPixelOfNamedColor("Blue"); + Color color = TestUtils.GetColorByName("Blue"); provider.RunValidatingProcessorTest(c => c.Fill(color, region), testDetails, ImageComparer.Exact); } @@ -101,7 +101,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing { FormattableString testDetails = $"(x{x0},y{y0},w{w},h{h})"; var region = new RectangleF(x0, y0, w, h); - TPixel color = TestUtils.GetPixelOfNamedColor("Blue"); + Color color = TestUtils.GetColorByName("Blue"); provider.RunValidatingProcessorTestOnWrappedMemoryImage( c => c.Fill(color, region), @@ -150,11 +150,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing float blendPercentage) where TPixel : struct, IPixel { - var vec = TestUtils.GetPixelOfNamedColor(newColorName).ToVector4(); - vec.W = alpha; - - TPixel fillColor = default; - fillColor.FromVector4(vec); + Color fillColor = TestUtils.GetColorByName(newColorName).WithAlpha(alpha); using (Image image = provider.GetImage()) { @@ -169,11 +165,11 @@ namespace SixLabors.ImageSharp.Tests.Drawing { var region = new ShapeRegion(new RectangularPolygon(0, 0, 16, 16)); - image.Mutate(c => c.Fill(options, new SolidBrush(fillColor), region)); + image.Mutate(c => c.Fill(options, new SolidBrush(fillColor), region)); } else { - image.Mutate(c => c.Fill(options, new SolidBrush(fillColor))); + image.Mutate(c => c.Fill(options, new SolidBrush(fillColor))); } var testOutputDetails = new @@ -194,7 +190,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing PixelBlender blender = PixelOperations.Instance.GetPixelBlender( blenderMode, PixelAlphaCompositionMode.SrcOver); - TPixel expectedPixel = blender.Blend(bgColor, fillColor, blendPercentage); + TPixel expectedPixel = blender.Blend(bgColor, fillColor.ToPixel(), blendPercentage); image.ComparePixelBufferTo(expectedPixel); } diff --git a/tests/ImageSharp.Tests/Drawing/Paths/DrawPathCollection.cs b/tests/ImageSharp.Tests/Drawing/Paths/DrawPathCollection.cs index 326517a4e1..5ab7894355 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/DrawPathCollection.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/DrawPathCollection.cs @@ -14,8 +14,8 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths public class DrawPathCollection : BaseImageOperationsExtensionTest { GraphicsOptions noneDefault = new GraphicsOptions(); - Rgba32 color = Rgba32.HotPink; - Pen pen = Pens.Solid(Rgba32.HotPink, 1); + Color color = Color.HotPink; + Pen pen = Pens.Solid(Rgba32.HotPink, 1); IPath path1 = new Path(new LinearLineSegment(new SixLabors.Primitives.PointF[] { new Vector2(10,10), new Vector2(20,10), @@ -43,7 +43,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths for (int i = 0; i < 2; i++) { - FillRegionProcessor processor = this.Verify>(i); + FillRegionProcessor processor = this.Verify(i); Assert.Equal(GraphicsOptions.Default, processor.Options); @@ -63,7 +63,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths for (int i = 0; i < 2; i++) { - FillRegionProcessor processor = this.Verify>(i); + FillRegionProcessor processor = this.Verify(i); Assert.Equal(this.noneDefault, processor.Options); @@ -81,14 +81,14 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths for (int i = 0; i < 2; i++) { - FillRegionProcessor processor = this.Verify>(i); + FillRegionProcessor processor = this.Verify(i); Assert.Equal(GraphicsOptions.Default, processor.Options); ShapePath region = Assert.IsType(processor.Region); ComplexPolygon polygon = Assert.IsType(region.Shape); - SolidBrush brush = Assert.IsType>(processor.Brush); + SolidBrush brush = Assert.IsType(processor.Brush); Assert.Equal(this.color, brush.Color); } } @@ -100,14 +100,14 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths for (int i = 0; i < 2; i++) { - FillRegionProcessor processor = this.Verify>(i); + FillRegionProcessor processor = this.Verify(i); Assert.Equal(this.noneDefault, processor.Options); ShapePath region = Assert.IsType(processor.Region); ComplexPolygon polygon = Assert.IsType(region.Shape); - SolidBrush brush = Assert.IsType>(processor.Brush); + SolidBrush brush = Assert.IsType(processor.Brush); Assert.Equal(this.color, brush.Color); } } diff --git a/tests/ImageSharp.Tests/Drawing/Paths/FillPath.cs b/tests/ImageSharp.Tests/Drawing/Paths/FillPath.cs index e72fbbdf24..d65dcd8c64 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/FillPath.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/FillPath.cs @@ -14,8 +14,8 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths public class FillPath : BaseImageOperationsExtensionTest { GraphicsOptions noneDefault = new GraphicsOptions(); - Rgba32 color = Rgba32.HotPink; - SolidBrush brush = Brushes.Solid(Rgba32.HotPink); + Color color = Color.HotPink; + SolidBrush brush = Brushes.Solid(Rgba32.HotPink); IPath path = new Path(new LinearLineSegment(new SixLabors.Primitives.PointF[] { new Vector2(10,10), new Vector2(20,10), @@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths public void CorrectlySetsBrushAndPath() { this.operations.Fill(this.brush, this.path); - var processor = this.Verify>(); + var processor = this.Verify(); Assert.Equal(GraphicsOptions.Default, processor.Options); @@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths public void CorrectlySetsBrushPathOptions() { this.operations.Fill(this.noneDefault, this.brush, this.path); - var processor = this.Verify>(); + var processor = this.Verify(); Assert.Equal(this.noneDefault, processor.Options); @@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths public void CorrectlySetsColorAndPath() { this.operations.Fill(this.color, this.path); - var processor = this.Verify>(); + var processor = this.Verify(); Assert.Equal(GraphicsOptions.Default, processor.Options); @@ -67,7 +67,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths Polygon polygon = Assert.IsType(region.Shape); LinearLineSegment segments = Assert.IsType(polygon.LineSegments[0]); - SolidBrush brush = Assert.IsType>(processor.Brush); + SolidBrush brush = Assert.IsType(processor.Brush); Assert.Equal(this.color, brush.Color); } @@ -75,7 +75,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths public void CorrectlySetsColorPathAndOptions() { this.operations.Fill(this.noneDefault, this.color, this.path); - var processor = this.Verify>(); + var processor = this.Verify(); Assert.Equal(this.noneDefault, processor.Options); @@ -83,7 +83,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths Polygon polygon = Assert.IsType(region.Shape); LinearLineSegment segments = Assert.IsType(polygon.LineSegments[0]); - SolidBrush brush = Assert.IsType>(processor.Brush); + SolidBrush brush = Assert.IsType(processor.Brush); Assert.Equal(this.color, brush.Color); } } diff --git a/tests/ImageSharp.Tests/Drawing/Paths/FillPathCollection.cs b/tests/ImageSharp.Tests/Drawing/Paths/FillPathCollection.cs index ec7a5a20c8..1f8e2d423d 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/FillPathCollection.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/FillPathCollection.cs @@ -14,8 +14,8 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths public class FillPathCollection : BaseImageOperationsExtensionTest { GraphicsOptions noneDefault = new GraphicsOptions(); - Rgba32 color = Rgba32.HotPink; - SolidBrush brush = Brushes.Solid(Rgba32.HotPink); + Color color = Color.HotPink; + SolidBrush brush = Brushes.Solid(Rgba32.HotPink); IPath path1 = new Path(new LinearLineSegment(new SixLabors.Primitives.PointF[] { new Vector2(10,10), new Vector2(20,10), @@ -43,7 +43,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths for (int i = 0; i < 2; i++) { - FillRegionProcessor processor = this.Verify>(i); + FillRegionProcessor processor = this.Verify(i); Assert.Equal(GraphicsOptions.Default, processor.Options); @@ -64,7 +64,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths for (int i = 0; i < 2; i++) { - FillRegionProcessor processor = this.Verify>(i); + FillRegionProcessor processor = this.Verify(i); Assert.Equal(this.noneDefault, processor.Options); @@ -83,7 +83,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths for (int i = 0; i < 2; i++) { - FillRegionProcessor processor = this.Verify>(i); + FillRegionProcessor processor = this.Verify(i); Assert.Equal(GraphicsOptions.Default, processor.Options); @@ -91,7 +91,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths Polygon polygon = Assert.IsType(region.Shape); LinearLineSegment segments = Assert.IsType(polygon.LineSegments[0]); - SolidBrush brush = Assert.IsType>(processor.Brush); + SolidBrush brush = Assert.IsType(processor.Brush); Assert.Equal(this.color, brush.Color); } } @@ -103,7 +103,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths for (int i = 0; i < 2; i++) { - FillRegionProcessor processor = this.Verify>(i); + FillRegionProcessor processor = this.Verify(i); Assert.Equal(this.noneDefault, processor.Options); @@ -111,7 +111,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths Polygon polygon = Assert.IsType(region.Shape); LinearLineSegment segments = Assert.IsType(polygon.LineSegments[0]); - SolidBrush brush = Assert.IsType>(processor.Brush); + SolidBrush brush = Assert.IsType(processor.Brush); Assert.Equal(this.color, brush.Color); } } diff --git a/tests/ImageSharp.Tests/Drawing/Paths/FillPolygon.cs b/tests/ImageSharp.Tests/Drawing/Paths/FillPolygon.cs index d8927a4683..b270a03cfc 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/FillPolygon.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/FillPolygon.cs @@ -14,8 +14,8 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths public class FillPolygon : BaseImageOperationsExtensionTest { GraphicsOptions noneDefault = new GraphicsOptions(); - Rgba32 color = Rgba32.HotPink; - SolidBrush brush = Brushes.Solid(Rgba32.HotPink); + Color color = Color.HotPink; + SolidBrush brush = Brushes.Solid(Rgba32.HotPink); SixLabors.Primitives.PointF[] path = { new Vector2(10,10), new Vector2(20,10), @@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths { this.operations.FillPolygon(this.brush, this.path); - FillRegionProcessor processor = this.Verify>(); + FillRegionProcessor processor = this.Verify(); Assert.Equal(GraphicsOptions.Default, processor.Options); @@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths public void CorrectlySetsBrushPathAndOptions() { this.operations.FillPolygon(this.noneDefault, this.brush, this.path); - FillRegionProcessor processor = this.Verify>(); + FillRegionProcessor processor = this.Verify(); Assert.Equal(this.noneDefault, processor.Options); @@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths public void CorrectlySetsColorAndPath() { this.operations.FillPolygon(this.color, this.path); - FillRegionProcessor processor = this.Verify>(); + FillRegionProcessor processor = this.Verify(); Assert.Equal(GraphicsOptions.Default, processor.Options); @@ -68,7 +68,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths Polygon polygon = Assert.IsType(region.Shape); LinearLineSegment segemnt = Assert.IsType(polygon.LineSegments[0]); - SolidBrush brush = Assert.IsType>(processor.Brush); + SolidBrush brush = Assert.IsType(processor.Brush); Assert.Equal(this.color, brush.Color); } @@ -76,7 +76,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths public void CorrectlySetsColorPathAndOptions() { this.operations.FillPolygon(this.noneDefault, this.color, this.path); - FillRegionProcessor processor = this.Verify>(); + FillRegionProcessor processor = this.Verify(); Assert.Equal(this.noneDefault, processor.Options); @@ -84,7 +84,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths Polygon polygon = Assert.IsType(region.Shape); LinearLineSegment segemnt = Assert.IsType(polygon.LineSegments[0]); - SolidBrush brush = Assert.IsType>(processor.Brush); + SolidBrush brush = Assert.IsType(processor.Brush); Assert.Equal(this.color, brush.Color); } } diff --git a/tests/ImageSharp.Tests/Drawing/Paths/FillRectangle.cs b/tests/ImageSharp.Tests/Drawing/Paths/FillRectangle.cs index 8f648e425f..6cb052aa8f 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/FillRectangle.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/FillRectangle.cs @@ -12,15 +12,15 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths public class FillRectangle : BaseImageOperationsExtensionTest { GraphicsOptions noneDefault = new GraphicsOptions(); - Rgba32 color = Rgba32.HotPink; - SolidBrush brush = Brushes.Solid(Rgba32.HotPink); + Color color = Color.HotPink; + SolidBrush brush = Brushes.Solid(Rgba32.HotPink); SixLabors.Primitives.Rectangle rectangle = new SixLabors.Primitives.Rectangle(10, 10, 77, 76); [Fact] public void CorrectlySetsBrushAndRectangle() { this.operations.Fill(this.brush, this.rectangle); - FillRegionProcessor processor = this.Verify>(); + FillRegionProcessor processor = this.Verify(); Assert.Equal(GraphicsOptions.Default, processor.Options); @@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths public void CorrectlySetsBrushRectangleAndOptions() { this.operations.Fill(this.noneDefault, this.brush, this.rectangle); - FillRegionProcessor processor = this.Verify>(); + FillRegionProcessor processor = this.Verify(); Assert.Equal(this.noneDefault, processor.Options); @@ -56,7 +56,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths public void CorrectlySetsColorAndRectangle() { this.operations.Fill(this.color, this.rectangle); - FillRegionProcessor processor = this.Verify>(); + FillRegionProcessor processor = this.Verify(); Assert.Equal(GraphicsOptions.Default, processor.Options); @@ -67,7 +67,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths Assert.Equal(rect.Size.Width, this.rectangle.Width); Assert.Equal(rect.Size.Height, this.rectangle.Height); - SolidBrush brush = Assert.IsType>(processor.Brush); + SolidBrush brush = Assert.IsType(processor.Brush); Assert.Equal(this.color, brush.Color); } @@ -75,7 +75,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths public void CorrectlySetsColorRectangleAndOptions() { this.operations.Fill(this.noneDefault, this.color, this.rectangle); - FillRegionProcessor processor = this.Verify>(); + FillRegionProcessor processor = this.Verify(); Assert.Equal(this.noneDefault, processor.Options); @@ -86,7 +86,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths Assert.Equal(rect.Size.Width, this.rectangle.Width); Assert.Equal(rect.Size.Height, this.rectangle.Height); - SolidBrush brush = Assert.IsType>(processor.Brush); + SolidBrush brush = Assert.IsType(processor.Brush); Assert.Equal(this.color, brush.Color); } } diff --git a/tests/ImageSharp.Tests/Drawing/RecolorImageTests.cs b/tests/ImageSharp.Tests/Drawing/RecolorImageTests.cs index 461ca700f7..fae0bf72b4 100644 --- a/tests/ImageSharp.Tests/Drawing/RecolorImageTests.cs +++ b/tests/ImageSharp.Tests/Drawing/RecolorImageTests.cs @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing { Color sourceColor = TestUtils.GetColorByName(sourceColorName); Color targetColor = TestUtils.GetColorByName(targetColorName); - var brush = new RecolorBrush(sourceColor.ToPixel(), targetColor.ToPixel(), threshold); + var brush = new RecolorBrush(sourceColor, targetColor, threshold); FormattableString testInfo = $"{sourceColorName}-{targetColorName}-{threshold}"; provider.RunValidatingProcessorTest(x => x.Fill(brush), testInfo); @@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing { Color sourceColor = TestUtils.GetColorByName(sourceColorName); Color targetColor = TestUtils.GetColorByName(targetColorName); - var brush = new RecolorBrush(sourceColor.ToPixel(), targetColor.ToPixel(), threshold); + var brush = new RecolorBrush(sourceColor, targetColor, threshold); FormattableString testInfo = $"{sourceColorName}-{targetColorName}-{threshold}"; provider.RunValidatingProcessorTest(x => diff --git a/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs b/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs index e8f53de010..fd8713cccd 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs @@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing using (Image image = provider.GetImage()) { image.Mutate(x => x.BackgroundColor(blue)); - image.Mutate(x => x.Fill(hotPink.ToPixel(), new Polygon(new CubicBezierLineSegment(simplePath)))); + image.Mutate(x => x.Fill(hotPink, new Polygon(new CubicBezierLineSegment(simplePath)))); image.DebugSave(provider); image.CompareToReferenceOutput(provider); } diff --git a/tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs b/tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs index 94e12f8581..c34d50297a 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs @@ -43,11 +43,11 @@ namespace SixLabors.ImageSharp.Tests.Drawing int scaleY = img.Height / 100; img.Mutate( x => x.Fill( - NamedColors.DarkBlue, + Color.DarkBlue, new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY) ) .Fill(new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode=composition }, - NamedColors.HotPink, + Color.HotPink, new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY)) ); @@ -69,17 +69,17 @@ namespace SixLabors.ImageSharp.Tests.Drawing int scaleY = img.Height / 100; img.Mutate( x => x.Fill( - NamedColors.DarkBlue, + Color.DarkBlue, new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY))); img.Mutate( x => x.Fill( new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition }, - NamedColors.HotPink, + Color.HotPink, new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY))); img.Mutate( x => x.Fill( new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition }, - NamedColors.Transparent, + Color.Transparent, new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY)) ); @@ -101,22 +101,20 @@ namespace SixLabors.ImageSharp.Tests.Drawing int scaleY = (img.Height / 100); img.Mutate( x => x.Fill( - NamedColors.DarkBlue, + Color.DarkBlue, new Rectangle(0 * scaleX, 40, 100 * scaleX, 20 * scaleY))); img.Mutate( x => x.Fill( new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition }, - NamedColors.HotPink, + Color.HotPink, new Rectangle(20 * scaleX, 0, 30 * scaleX, 100 * scaleY))); - var c = NamedColors.Red.ToVector4(); - c.W *= 0.5f; - var pixel = default(TPixel); - pixel.FromVector4(c); + + var transparentRed = Color.Red.WithAlpha(0.5f); img.Mutate( x => x.Fill( new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition }, - pixel, + transparentRed, new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY)) ); @@ -139,12 +137,12 @@ namespace SixLabors.ImageSharp.Tests.Drawing dstImg.Mutate( x => x.Fill( - NamedColors.DarkBlue, + Color.DarkBlue, new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY))); srcImg.Mutate( x => x.Fill( - NamedColors.Black, + Color.Black, new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY))); dstImg.Mutate( diff --git a/tests/ImageSharp.Tests/Drawing/Text/DrawText.cs b/tests/ImageSharp.Tests/Drawing/Text/DrawText.cs index 76f40e0aca..8520a2f3fb 100644 --- a/tests/ImageSharp.Tests/Drawing/Text/DrawText.cs +++ b/tests/ImageSharp.Tests/Drawing/Text/DrawText.cs @@ -14,9 +14,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text { public class DrawText : BaseImageOperationsExtensionTest { - Rgba32 color = Rgba32.HotPink; + Rgba32 color = Color.HotPink; - SolidBrush brush = Brushes.Solid(Rgba32.HotPink); + SolidBrush brush = Brushes.Solid(Color.HotPink); IPath path = new SixLabors.Shapes.Path( new LinearLineSegment( @@ -39,57 +39,57 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text new TextGraphicsOptions(true), "123", this.Font, - Brushes.Solid(Rgba32.Red), + Brushes.Solid(Color.Red), null, Vector2.Zero); - this.Verify>(0); + this.Verify(0); } [Fact] public void FillsForEachACharachterWhenBrushSetAndNotPenDefaultOptions() { - this.operations.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), null, Vector2.Zero); + this.operations.DrawText("123", this.Font, Brushes.Solid(Color.Red), null, Vector2.Zero); - this.Verify>(0); + this.Verify(0); } [Fact] public void FillsForEachACharachterWhenBrushSet() { - this.operations.DrawText(new TextGraphicsOptions(true), "123", this.Font, Brushes.Solid(Rgba32.Red), Vector2.Zero); + this.operations.DrawText(new TextGraphicsOptions(true), "123", this.Font, Brushes.Solid(Color.Red), Vector2.Zero); - this.Verify>(0); + this.Verify(0); } [Fact] public void FillsForEachACharachterWhenBrushSetDefaultOptions() { - this.operations.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), Vector2.Zero); + this.operations.DrawText("123", this.Font, Brushes.Solid(Color.Red), Vector2.Zero); - this.Verify>(0); + this.Verify(0); } [Fact] public void FillsForEachACharachterWhenColorSet() { - this.operations.DrawText(new TextGraphicsOptions(true), "123", this.Font, Rgba32.Red, Vector2.Zero); + this.operations.DrawText(new TextGraphicsOptions(true), "123", this.Font, Color.Red, Vector2.Zero); - var processor = this.Verify>(0); + var processor = this.Verify(0); - SolidBrush brush = Assert.IsType>(processor.Brush); - Assert.Equal(Rgba32.Red, brush.Color); + SolidBrush brush = Assert.IsType(processor.Brush); + Assert.Equal(Color.Red, brush.Color); } [Fact] public void FillsForEachACharachterWhenColorSetDefaultOptions() { - this.operations.DrawText("123", this.Font, Rgba32.Red, Vector2.Zero); + this.operations.DrawText("123", this.Font, Color.Red, Vector2.Zero); - var processor = this.Verify>(0); + var processor = this.Verify(0); - SolidBrush brush = Assert.IsType>(processor.Brush); - Assert.Equal(Rgba32.Red, brush.Color); + SolidBrush brush = Assert.IsType(processor.Brush); + Assert.Equal(Color.Red, brush.Color); } [Fact] @@ -100,39 +100,39 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text "123", this.Font, null, - Pens.Dash(Rgba32.Red, 1), + Pens.Dash(Color.Red, 1), Vector2.Zero); - var processor = this.Verify>(0); + var processor = this.Verify(0); } [Fact] public void DrawForEachACharachterWhenPenSetAndNotBrushDefaultOptions() { - this.operations.DrawText("123", this.Font, null, Pens.Dash(Rgba32.Red, 1), Vector2.Zero); + this.operations.DrawText("123", this.Font, null, Pens.Dash(Color.Red, 1), Vector2.Zero); - var processor = this.Verify>(0); + var processor = this.Verify(0); } [Fact] public void DrawForEachACharachterWhenPenSet() { - this.operations.DrawText(new TextGraphicsOptions(true), "123", this.Font, Pens.Dash(Rgba32.Red, 1), Vector2.Zero); + this.operations.DrawText(new TextGraphicsOptions(true), "123", this.Font, Pens.Dash(Color.Red, 1), Vector2.Zero); - var processor = this.Verify>(0); + var processor = this.Verify(0); } [Fact] public void DrawForEachACharachterWhenPenSetDefaultOptions() { - this.operations.DrawText("123", this.Font, Pens.Dash(Rgba32.Red, 1), Vector2.Zero); + this.operations.DrawText("123", this.Font, Pens.Dash(Color.Red, 1), Vector2.Zero); - var processor = this.Verify>(0); + var processor = this.Verify(0); Assert.Equal("123", processor.Text); Assert.Equal(this.Font, processor.Font); - var penBrush = Assert.IsType>(processor.Pen.StrokeFill); - Assert.Equal(Rgba32.Red, penBrush.Color); + var penBrush = Assert.IsType(processor.Pen.StrokeFill); + Assert.Equal(Color.Red, penBrush.Color); Assert.Equal(1, processor.Pen.StrokeWidth); Assert.Equal(PointF.Empty, processor.Location); } @@ -144,19 +144,19 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text new TextGraphicsOptions(true), "123", this.Font, - Brushes.Solid(Rgba32.Red), - Pens.Dash(Rgba32.Red, 1), + Brushes.Solid(Color.Red), + Pens.Dash(Color.Red, 1), Vector2.Zero); - var processor = this.Verify>(0); + var processor = this.Verify(0); Assert.Equal("123", processor.Text); Assert.Equal(this.Font, processor.Font); - var brush = Assert.IsType>(processor.Brush); - Assert.Equal(Rgba32.Red, brush.Color); + var brush = Assert.IsType(processor.Brush); + Assert.Equal(Color.Red, brush.Color); Assert.Equal(PointF.Empty, processor.Location); - var penBrush = Assert.IsType>(processor.Pen.StrokeFill); - Assert.Equal(Rgba32.Red, penBrush.Color); + var penBrush = Assert.IsType(processor.Pen.StrokeFill); + Assert.Equal(Color.Red, penBrush.Color); Assert.Equal(1, processor.Pen.StrokeWidth); } } diff --git a/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs b/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs index 20ccb25a54..ebd9cf6448 100644 --- a/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs +++ b/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs @@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text where TPixel : struct, IPixel { Font font = CreateFont("OpenSans-Regular.ttf", 36); - TPixel color = NamedColors.Black; + Color color = Color.Black; float padding = 5; var text = "A short piece of text"; @@ -84,7 +84,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text where TPixel : struct, IPixel { Font font = CreateFont(fontName, fontSize); - TPixel color = NamedColors.Black; + Color color = Color.Black; provider.VerifyOperation( TextDrawingComparer, @@ -129,7 +129,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text HorizontalAlignment = HorizontalAlignment.Left, }; - TPixel color = NamedColors.Black; + Color color = Color.Black; // Based on the reported 0.0270% difference with AccuracyMultiple = 8 // We should avoid quality regressions leading to higher difference! @@ -159,7 +159,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text where TPixel : struct, IPixel { Font font = CreateFont(fontName, fontSize); - TPixel color = NamedColors.Black; + Color color = Color.Black; provider.VerifyOperation( OutlinedTextDrawingComparer, @@ -186,7 +186,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text where TPixel : struct, IPixel { Font font = CreateFont(fontName, fontSize); - TPixel color = NamedColors.Black; + Color color = Color.Black; provider.VerifyOperation( OutlinedTextDrawingComparer, @@ -217,7 +217,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text var comparer = ImageComparer.TolerantPercentage(0.2f); provider.RunValidatingProcessorTest( - x => x.DrawText(textOptions, text, font, NamedColors.Black, new PointF(10, 50)), + x => x.DrawText(textOptions, text, font, Color.Black, new PointF(10, 50)), details, comparer, appendPixelTypeToFileName: false, diff --git a/tests/ImageSharp.Tests/Issues/Issue412.cs b/tests/ImageSharp.Tests/Issues/Issue412.cs index b0374ce1fa..f22d3a85cf 100644 --- a/tests/ImageSharp.Tests/Issues/Issue412.cs +++ b/tests/ImageSharp.Tests/Issues/Issue412.cs @@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Tests.Issues { context.DrawLines( new GraphicsOptions(false), - NamedColors.Black, + Color.Black, 1, new[] { @@ -31,7 +31,7 @@ namespace SixLabors.ImageSharp.Tests.Issues context.DrawLines( new GraphicsOptions(false), - NamedColors.Red, + Color.Red, 1, new[] { @@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp.Tests.Issues for (var x = 0; x < 40; x++) { - Assert.True(NamedColors.Red.Equals(image[x, y]), $"expected {NamedColors.Red} but found {image[x, y]} at [{x}, {y}]"); + Assert.True(Color.Red.Equals(image[x, y]), $"expected {Color.Red} but found {image[x, y]} at [{x}, {y}]"); } } } diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs index 1ff95f60d0..88b30ce34e 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs @@ -53,8 +53,7 @@ namespace SixLabors.ImageSharp.Tests public override Image GetImage() { Image image = base.GetImage(); - TPixel color = default(TPixel); - color.FromRgba32(new Rgba32(this.r, this.g, this.b, this.a)); + Color color = new Rgba32(this.r, this.g, this.b, this.a); image.Mutate(x => x.Fill(color)); return image; diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index c5aa1fb944..048c6347cd 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -484,6 +484,20 @@ namespace SixLabors.ImageSharp.Tests return image; } + + /// + /// All pixels in all frames should be exactly equal to 'expectedPixelColor.ToPixel()'. + /// + public static Image ComparePixelBufferTo(this Image image, Color expectedPixelColor) + where TPixel : struct, IPixel + { + foreach (ImageFrame imageFrame in image.Frames) + { + imageFrame.ComparePixelBufferTo(expectedPixelColor.ToPixel()); + } + + return image; + } /// /// All pixels in the frame should be exactly equal to 'expectedPixel'. From a224e8899337c462c588c9584f0243daf81cb728 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 12 May 2019 21:56:40 +0200 Subject: [PATCH 182/223] tests are passing --- .../Processing/Processors/Text/DrawTextProcessor.cs | 2 +- tests/ImageSharp.Tests/Drawing/FillLinearGradientBrushTests.cs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs b/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs index 016cf56dea..d1e28afeb8 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs @@ -78,7 +78,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text public IImageProcessor CreatePixelSpecificProcessor() where TPixel : struct, IPixel { - throw new NotImplementedException(); + return new DrawTextProcessor(this); } } diff --git a/tests/ImageSharp.Tests/Drawing/FillLinearGradientBrushTests.cs b/tests/ImageSharp.Tests/Drawing/FillLinearGradientBrushTests.cs index 5d1442e79c..361e7e70d1 100644 --- a/tests/ImageSharp.Tests/Drawing/FillLinearGradientBrushTests.cs +++ b/tests/ImageSharp.Tests/Drawing/FillLinearGradientBrushTests.cs @@ -176,7 +176,8 @@ namespace SixLabors.ImageSharp.Tests.Drawing // the result must be a black and white pattern, no other color should occur: Assert.All( Enumerable.Range(0, image.Width).Select(i => image[i, 0]), - color => Assert.True(color.Equals(black) || color.Equals(white))); + color => Assert.True( + color.Equals(black.ToPixel()) || color.Equals(white.ToPixel()))); image.CompareToReferenceOutput( TolerantComparer, From 2025f9845c3de2a732497cfd3e70dd4878e89833 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 12 May 2019 22:14:46 +0200 Subject: [PATCH 183/223] fix new 8bit bmp code --- src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs | 9 ++++++--- tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index 90ea673d3e..edde7dc893 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -306,12 +306,15 @@ namespace SixLabors.ImageSharp.Formats.Bmp where TPixel : struct, IPixel { using (IMemoryOwner colorPaletteBuffer = this.memoryAllocator.AllocateManagedByteBuffer(ColorPaletteSize8Bit, AllocationOptions.Clean)) - using (QuantizedFrame quantized = this.quantizer.CreateFrameQuantizer(this.configuration, 256).QuantizeFrame(image)) + using (IQuantizedFrame quantized = this.quantizer.CreateFrameQuantizer(this.configuration, 256).QuantizeFrame(image)) { Span colorPalette = colorPaletteBuffer.GetSpan(); int idx = 0; var color = default(Rgba32); - foreach (TPixel quantizedColor in quantized.Palette) + ReadOnlySpan paletteSpan = quantized.Palette.Span; + + // TODO: Use bulk conversion here for better perf + foreach (TPixel quantizedColor in paletteSpan) { quantizedColor.ToRgba32(ref color); colorPalette[idx] = color.B; @@ -327,7 +330,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp for (int y = image.Height - 1; y >= 0; y--) { - Span pixelSpan = quantized.GetRowSpan(y); + ReadOnlySpan pixelSpan = quantized.GetRowSpan(y); stream.Write(pixelSpan); for (int i = 0; i < this.padding; i++) diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs index abf57ed01a..dd76e9443c 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs @@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp { using static TestImages.Bmp; - public class BmpEncoderTests : FileTestBase + public class BmpEncoderTests { public static readonly TheoryData BitsPerPixel = new TheoryData From f5f80ca796de826f9a9becd1e26f9a12e7aa6dd7 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 12 May 2019 22:20:34 +0200 Subject: [PATCH 184/223] move drawing extensions to a (non-namespace-provider) subfolder --- .../ImageSharp.Drawing.csproj.DotSettings | 2 + .../{ => Extensions}/DrawBezierExtensions.cs | 0 .../{ => Extensions}/DrawImageExtensions.cs | 272 +++++++++--------- .../{ => Extensions}/DrawLineExtensions.cs | 0 .../DrawPathCollectionExtensions.cs | 0 .../{ => Extensions}/DrawPathExtensions.cs | 0 .../{ => Extensions}/DrawPolygonExtensions.cs | 0 .../DrawRectangleExtensions.cs | 0 .../{ => Extensions}/DrawTextExtensions.cs | 0 .../FillPathBuilderExtensions.cs | 0 .../FillPathCollectionExtensions.cs | 0 .../{ => Extensions}/FillPathExtensions.cs | 0 .../{ => Extensions}/FillPolygonExtensions.cs | 0 .../FillRectangleExtensions.cs | 0 .../{ => Extensions}/FillRegionExtensions.cs | 0 15 files changed, 138 insertions(+), 136 deletions(-) create mode 100644 src/ImageSharp.Drawing/ImageSharp.Drawing.csproj.DotSettings rename src/ImageSharp.Drawing/Processing/{ => Extensions}/DrawBezierExtensions.cs (100%) rename src/ImageSharp.Drawing/Processing/{ => Extensions}/DrawImageExtensions.cs (98%) rename src/ImageSharp.Drawing/Processing/{ => Extensions}/DrawLineExtensions.cs (100%) rename src/ImageSharp.Drawing/Processing/{ => Extensions}/DrawPathCollectionExtensions.cs (100%) rename src/ImageSharp.Drawing/Processing/{ => Extensions}/DrawPathExtensions.cs (100%) rename src/ImageSharp.Drawing/Processing/{ => Extensions}/DrawPolygonExtensions.cs (100%) rename src/ImageSharp.Drawing/Processing/{ => Extensions}/DrawRectangleExtensions.cs (100%) rename src/ImageSharp.Drawing/Processing/{ => Extensions}/DrawTextExtensions.cs (100%) rename src/ImageSharp.Drawing/Processing/{ => Extensions}/FillPathBuilderExtensions.cs (100%) rename src/ImageSharp.Drawing/Processing/{ => Extensions}/FillPathCollectionExtensions.cs (100%) rename src/ImageSharp.Drawing/Processing/{ => Extensions}/FillPathExtensions.cs (100%) rename src/ImageSharp.Drawing/Processing/{ => Extensions}/FillPolygonExtensions.cs (100%) rename src/ImageSharp.Drawing/Processing/{ => Extensions}/FillRectangleExtensions.cs (100%) rename src/ImageSharp.Drawing/Processing/{ => Extensions}/FillRegionExtensions.cs (100%) diff --git a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj.DotSettings b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj.DotSettings new file mode 100644 index 0000000000..a728b54979 --- /dev/null +++ b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj.DotSettings @@ -0,0 +1,2 @@ + + True \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/DrawBezierExtensions.cs b/src/ImageSharp.Drawing/Processing/Extensions/DrawBezierExtensions.cs similarity index 100% rename from src/ImageSharp.Drawing/Processing/DrawBezierExtensions.cs rename to src/ImageSharp.Drawing/Processing/Extensions/DrawBezierExtensions.cs diff --git a/src/ImageSharp.Drawing/Processing/DrawImageExtensions.cs b/src/ImageSharp.Drawing/Processing/Extensions/DrawImageExtensions.cs similarity index 98% rename from src/ImageSharp.Drawing/Processing/DrawImageExtensions.cs rename to src/ImageSharp.Drawing/Processing/Extensions/DrawImageExtensions.cs index a8ee4d90bc..58459ebc5d 100644 --- a/src/ImageSharp.Drawing/Processing/DrawImageExtensions.cs +++ b/src/ImageSharp.Drawing/Processing/Extensions/DrawImageExtensions.cs @@ -1,137 +1,137 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing.Processors.Drawing; -using SixLabors.Primitives; - -namespace SixLabors.ImageSharp.Processing -{ - /// - /// Adds extensions that allow the drawing of images to the type. - /// - public static class DrawImageExtensions - { - /// - /// Draws the given image together with the current one by blending their pixels. - /// - /// The pixel format of the destination image. - /// The pixel format of the source image. - /// The image this method extends. - /// The image to blend with the currently processing image. - /// The opacity of the image to blend. Must be between 0 and 1. - /// The . - public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, float opacity) - where TPixelDst : struct, IPixel - where TPixelSrc : struct, IPixel - => source.ApplyProcessor(new DrawImageProcessor(image, Point.Empty, GraphicsOptions.Default.ColorBlendingMode, GraphicsOptions.Default.AlphaCompositionMode, opacity)); - - /// - /// Draws the given image together with the current one by blending their pixels. - /// - /// The pixel format of the destination image. - /// The pixel format of the source image. - /// The image this method extends. - /// The image to blend with the currently processing image. - /// The blending mode. - /// The opacity of the image to blend. Must be between 0 and 1. - /// The . - public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, PixelColorBlendingMode colorBlending, float opacity) - where TPixelDst : struct, IPixel - where TPixelSrc : struct, IPixel - => source.ApplyProcessor(new DrawImageProcessor(image, Point.Empty, colorBlending, GraphicsOptions.Default.AlphaCompositionMode, opacity)); - - /// - /// Draws the given image together with the current one by blending their pixels. - /// - /// The pixel format of the destination image. - /// The pixel format of the source image. - /// The image this method extends. - /// The image to blend with the currently processing image. - /// The color blending mode. - /// The alpha composition mode. - /// The opacity of the image to blend. Must be between 0 and 1. - /// The . - public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, float opacity) - where TPixelDst : struct, IPixel - where TPixelSrc : struct, IPixel - => source.ApplyProcessor(new DrawImageProcessor(image, Point.Empty, colorBlending, alphaComposition, opacity)); - - /// - /// Draws the given image together with the current one by blending their pixels. - /// - /// The pixel format of the destination image. - /// The pixel format of the source image. - /// The image this method extends. - /// The image to blend with the currently processing image. - /// The options, including the blending type and blending amount. - /// The . - public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, GraphicsOptions options) - where TPixelDst : struct, IPixel - where TPixelSrc : struct, IPixel - => source.ApplyProcessor(new DrawImageProcessor(image, Point.Empty, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage)); - - /// - /// Draws the given image together with the current one by blending their pixels. - /// - /// The pixel format of the destination image. - /// The pixel format of the source image. - /// The image this method extends. - /// The image to blend with the currently processing image. - /// The location to draw the blended image. - /// The opacity of the image to blend. Must be between 0 and 1. - /// The . - public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, Point location, float opacity) - where TPixelDst : struct, IPixel - where TPixelSrc : struct, IPixel - => source.ApplyProcessor(new DrawImageProcessor(image, location, GraphicsOptions.Default.ColorBlendingMode, GraphicsOptions.Default.AlphaCompositionMode, opacity)); - - /// - /// Draws the given image together with the current one by blending their pixels. - /// - /// The pixel format of the destination image. - /// The pixel format of the source image. - /// The image this method extends. - /// The image to blend with the currently processing image. - /// The location to draw the blended image. - /// The color blending to apply. - /// The opacity of the image to blend. Must be between 0 and 1. - /// The . - public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, Point location, PixelColorBlendingMode colorBlending, float opacity) - where TPixelDst : struct, IPixel - where TPixelSrc : struct, IPixel - => source.ApplyProcessor(new DrawImageProcessor(image, location, colorBlending, GraphicsOptions.Default.AlphaCompositionMode, opacity)); - - /// - /// Draws the given image together with the current one by blending their pixels. - /// - /// The pixel format of the destination image. - /// The pixel format of the source image. - /// The image this method extends. - /// The image to blend with the currently processing image. - /// The location to draw the blended image. - /// The color blending to apply. - /// The alpha composition mode. - /// The opacity of the image to blend. Must be between 0 and 1. - /// The . - public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, Point location, PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, float opacity) - where TPixelDst : struct, IPixel - where TPixelSrc : struct, IPixel - => source.ApplyProcessor(new DrawImageProcessor(image, location, colorBlending, alphaComposition, opacity)); - - /// - /// Draws the given image together with the current one by blending their pixels. - /// - /// The pixel format of the destination image. - /// The pixel format of the source image. - /// The image this method extends. - /// The image to blend with the currently processing image. - /// The location to draw the blended image. - /// The options containing the blend mode and opacity. - /// The . - public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, Point location, GraphicsOptions options) - where TPixelDst : struct, IPixel - where TPixelSrc : struct, IPixel - => source.ApplyProcessor(new DrawImageProcessor(image, location, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage)); - } +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors.Drawing; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing +{ + /// + /// Adds extensions that allow the drawing of images to the type. + /// + public static class DrawImageExtensions + { + /// + /// Draws the given image together with the current one by blending their pixels. + /// + /// The pixel format of the destination image. + /// The pixel format of the source image. + /// The image this method extends. + /// The image to blend with the currently processing image. + /// The opacity of the image to blend. Must be between 0 and 1. + /// The . + public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, float opacity) + where TPixelDst : struct, IPixel + where TPixelSrc : struct, IPixel + => source.ApplyProcessor(new DrawImageProcessor(image, Point.Empty, GraphicsOptions.Default.ColorBlendingMode, GraphicsOptions.Default.AlphaCompositionMode, opacity)); + + /// + /// Draws the given image together with the current one by blending their pixels. + /// + /// The pixel format of the destination image. + /// The pixel format of the source image. + /// The image this method extends. + /// The image to blend with the currently processing image. + /// The blending mode. + /// The opacity of the image to blend. Must be between 0 and 1. + /// The . + public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, PixelColorBlendingMode colorBlending, float opacity) + where TPixelDst : struct, IPixel + where TPixelSrc : struct, IPixel + => source.ApplyProcessor(new DrawImageProcessor(image, Point.Empty, colorBlending, GraphicsOptions.Default.AlphaCompositionMode, opacity)); + + /// + /// Draws the given image together with the current one by blending their pixels. + /// + /// The pixel format of the destination image. + /// The pixel format of the source image. + /// The image this method extends. + /// The image to blend with the currently processing image. + /// The color blending mode. + /// The alpha composition mode. + /// The opacity of the image to blend. Must be between 0 and 1. + /// The . + public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, float opacity) + where TPixelDst : struct, IPixel + where TPixelSrc : struct, IPixel + => source.ApplyProcessor(new DrawImageProcessor(image, Point.Empty, colorBlending, alphaComposition, opacity)); + + /// + /// Draws the given image together with the current one by blending their pixels. + /// + /// The pixel format of the destination image. + /// The pixel format of the source image. + /// The image this method extends. + /// The image to blend with the currently processing image. + /// The options, including the blending type and blending amount. + /// The . + public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, GraphicsOptions options) + where TPixelDst : struct, IPixel + where TPixelSrc : struct, IPixel + => source.ApplyProcessor(new DrawImageProcessor(image, Point.Empty, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage)); + + /// + /// Draws the given image together with the current one by blending their pixels. + /// + /// The pixel format of the destination image. + /// The pixel format of the source image. + /// The image this method extends. + /// The image to blend with the currently processing image. + /// The location to draw the blended image. + /// The opacity of the image to blend. Must be between 0 and 1. + /// The . + public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, Point location, float opacity) + where TPixelDst : struct, IPixel + where TPixelSrc : struct, IPixel + => source.ApplyProcessor(new DrawImageProcessor(image, location, GraphicsOptions.Default.ColorBlendingMode, GraphicsOptions.Default.AlphaCompositionMode, opacity)); + + /// + /// Draws the given image together with the current one by blending their pixels. + /// + /// The pixel format of the destination image. + /// The pixel format of the source image. + /// The image this method extends. + /// The image to blend with the currently processing image. + /// The location to draw the blended image. + /// The color blending to apply. + /// The opacity of the image to blend. Must be between 0 and 1. + /// The . + public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, Point location, PixelColorBlendingMode colorBlending, float opacity) + where TPixelDst : struct, IPixel + where TPixelSrc : struct, IPixel + => source.ApplyProcessor(new DrawImageProcessor(image, location, colorBlending, GraphicsOptions.Default.AlphaCompositionMode, opacity)); + + /// + /// Draws the given image together with the current one by blending their pixels. + /// + /// The pixel format of the destination image. + /// The pixel format of the source image. + /// The image this method extends. + /// The image to blend with the currently processing image. + /// The location to draw the blended image. + /// The color blending to apply. + /// The alpha composition mode. + /// The opacity of the image to blend. Must be between 0 and 1. + /// The . + public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, Point location, PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, float opacity) + where TPixelDst : struct, IPixel + where TPixelSrc : struct, IPixel + => source.ApplyProcessor(new DrawImageProcessor(image, location, colorBlending, alphaComposition, opacity)); + + /// + /// Draws the given image together with the current one by blending their pixels. + /// + /// The pixel format of the destination image. + /// The pixel format of the source image. + /// The image this method extends. + /// The image to blend with the currently processing image. + /// The location to draw the blended image. + /// The options containing the blend mode and opacity. + /// The . + public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, Point location, GraphicsOptions options) + where TPixelDst : struct, IPixel + where TPixelSrc : struct, IPixel + => source.ApplyProcessor(new DrawImageProcessor(image, location, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage)); + } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/DrawLineExtensions.cs b/src/ImageSharp.Drawing/Processing/Extensions/DrawLineExtensions.cs similarity index 100% rename from src/ImageSharp.Drawing/Processing/DrawLineExtensions.cs rename to src/ImageSharp.Drawing/Processing/Extensions/DrawLineExtensions.cs diff --git a/src/ImageSharp.Drawing/Processing/DrawPathCollectionExtensions.cs b/src/ImageSharp.Drawing/Processing/Extensions/DrawPathCollectionExtensions.cs similarity index 100% rename from src/ImageSharp.Drawing/Processing/DrawPathCollectionExtensions.cs rename to src/ImageSharp.Drawing/Processing/Extensions/DrawPathCollectionExtensions.cs diff --git a/src/ImageSharp.Drawing/Processing/DrawPathExtensions.cs b/src/ImageSharp.Drawing/Processing/Extensions/DrawPathExtensions.cs similarity index 100% rename from src/ImageSharp.Drawing/Processing/DrawPathExtensions.cs rename to src/ImageSharp.Drawing/Processing/Extensions/DrawPathExtensions.cs diff --git a/src/ImageSharp.Drawing/Processing/DrawPolygonExtensions.cs b/src/ImageSharp.Drawing/Processing/Extensions/DrawPolygonExtensions.cs similarity index 100% rename from src/ImageSharp.Drawing/Processing/DrawPolygonExtensions.cs rename to src/ImageSharp.Drawing/Processing/Extensions/DrawPolygonExtensions.cs diff --git a/src/ImageSharp.Drawing/Processing/DrawRectangleExtensions.cs b/src/ImageSharp.Drawing/Processing/Extensions/DrawRectangleExtensions.cs similarity index 100% rename from src/ImageSharp.Drawing/Processing/DrawRectangleExtensions.cs rename to src/ImageSharp.Drawing/Processing/Extensions/DrawRectangleExtensions.cs diff --git a/src/ImageSharp.Drawing/Processing/DrawTextExtensions.cs b/src/ImageSharp.Drawing/Processing/Extensions/DrawTextExtensions.cs similarity index 100% rename from src/ImageSharp.Drawing/Processing/DrawTextExtensions.cs rename to src/ImageSharp.Drawing/Processing/Extensions/DrawTextExtensions.cs diff --git a/src/ImageSharp.Drawing/Processing/FillPathBuilderExtensions.cs b/src/ImageSharp.Drawing/Processing/Extensions/FillPathBuilderExtensions.cs similarity index 100% rename from src/ImageSharp.Drawing/Processing/FillPathBuilderExtensions.cs rename to src/ImageSharp.Drawing/Processing/Extensions/FillPathBuilderExtensions.cs diff --git a/src/ImageSharp.Drawing/Processing/FillPathCollectionExtensions.cs b/src/ImageSharp.Drawing/Processing/Extensions/FillPathCollectionExtensions.cs similarity index 100% rename from src/ImageSharp.Drawing/Processing/FillPathCollectionExtensions.cs rename to src/ImageSharp.Drawing/Processing/Extensions/FillPathCollectionExtensions.cs diff --git a/src/ImageSharp.Drawing/Processing/FillPathExtensions.cs b/src/ImageSharp.Drawing/Processing/Extensions/FillPathExtensions.cs similarity index 100% rename from src/ImageSharp.Drawing/Processing/FillPathExtensions.cs rename to src/ImageSharp.Drawing/Processing/Extensions/FillPathExtensions.cs diff --git a/src/ImageSharp.Drawing/Processing/FillPolygonExtensions.cs b/src/ImageSharp.Drawing/Processing/Extensions/FillPolygonExtensions.cs similarity index 100% rename from src/ImageSharp.Drawing/Processing/FillPolygonExtensions.cs rename to src/ImageSharp.Drawing/Processing/Extensions/FillPolygonExtensions.cs diff --git a/src/ImageSharp.Drawing/Processing/FillRectangleExtensions.cs b/src/ImageSharp.Drawing/Processing/Extensions/FillRectangleExtensions.cs similarity index 100% rename from src/ImageSharp.Drawing/Processing/FillRectangleExtensions.cs rename to src/ImageSharp.Drawing/Processing/Extensions/FillRectangleExtensions.cs diff --git a/src/ImageSharp.Drawing/Processing/FillRegionExtensions.cs b/src/ImageSharp.Drawing/Processing/Extensions/FillRegionExtensions.cs similarity index 100% rename from src/ImageSharp.Drawing/Processing/FillRegionExtensions.cs rename to src/ImageSharp.Drawing/Processing/Extensions/FillRegionExtensions.cs From e57fe2058a27b12b003081b63820484f4bc54147 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 12 May 2019 22:24:27 +0200 Subject: [PATCH 185/223] rename files --- .../Processing/{ColorStop{TPixel}.cs => ColorStop.cs} | 0 ...cGradientBrush{TPixel}.cs => EllipticGradientBrush.cs} | 0 ...{GradientBrushBase{TPixel}.cs => GradientBrushBase.cs} | 0 .../Processing/{ImageBrush{TPixel}.cs => ImageBrush.cs} | 7 +++---- ...earGradientBrush{TPixel}.cs => LinearGradientBrush.cs} | 1 - .../{PatternBrush{TPixel}.cs => PatternBrush.cs} | 0 .../Processing/{Pen{TPixel}.cs => Pen.cs} | 8 ++++---- ...ialGradientBrush{TPixel}.cs => RadialGradientBrush.cs} | 0 .../{RecolorBrush{TPixel}.cs => RecolorBrush.cs} | 1 - .../Processing/{SolidBrush{TPixel}.cs => SolidBrush.cs} | 0 tests/ImageSharp.Tests/Drawing/FillImageBrushTests.cs | 2 +- tests/ImageSharp.Tests/Drawing/FillPolygonTests.cs | 2 +- 12 files changed, 9 insertions(+), 12 deletions(-) rename src/ImageSharp.Drawing/Processing/{ColorStop{TPixel}.cs => ColorStop.cs} (100%) rename src/ImageSharp.Drawing/Processing/{EllipticGradientBrush{TPixel}.cs => EllipticGradientBrush.cs} (100%) rename src/ImageSharp.Drawing/Processing/{GradientBrushBase{TPixel}.cs => GradientBrushBase.cs} (100%) rename src/ImageSharp.Drawing/Processing/{ImageBrush{TPixel}.cs => ImageBrush.cs} (97%) rename src/ImageSharp.Drawing/Processing/{LinearGradientBrush{TPixel}.cs => LinearGradientBrush.cs} (98%) rename src/ImageSharp.Drawing/Processing/{PatternBrush{TPixel}.cs => PatternBrush.cs} (100%) rename src/ImageSharp.Drawing/Processing/{Pen{TPixel}.cs => Pen.cs} (88%) rename src/ImageSharp.Drawing/Processing/{RadialGradientBrush{TPixel}.cs => RadialGradientBrush.cs} (100%) rename src/ImageSharp.Drawing/Processing/{RecolorBrush{TPixel}.cs => RecolorBrush.cs} (99%) rename src/ImageSharp.Drawing/Processing/{SolidBrush{TPixel}.cs => SolidBrush.cs} (100%) diff --git a/src/ImageSharp.Drawing/Processing/ColorStop{TPixel}.cs b/src/ImageSharp.Drawing/Processing/ColorStop.cs similarity index 100% rename from src/ImageSharp.Drawing/Processing/ColorStop{TPixel}.cs rename to src/ImageSharp.Drawing/Processing/ColorStop.cs diff --git a/src/ImageSharp.Drawing/Processing/EllipticGradientBrush{TPixel}.cs b/src/ImageSharp.Drawing/Processing/EllipticGradientBrush.cs similarity index 100% rename from src/ImageSharp.Drawing/Processing/EllipticGradientBrush{TPixel}.cs rename to src/ImageSharp.Drawing/Processing/EllipticGradientBrush.cs diff --git a/src/ImageSharp.Drawing/Processing/GradientBrushBase{TPixel}.cs b/src/ImageSharp.Drawing/Processing/GradientBrushBase.cs similarity index 100% rename from src/ImageSharp.Drawing/Processing/GradientBrushBase{TPixel}.cs rename to src/ImageSharp.Drawing/Processing/GradientBrushBase.cs diff --git a/src/ImageSharp.Drawing/Processing/ImageBrush{TPixel}.cs b/src/ImageSharp.Drawing/Processing/ImageBrush.cs similarity index 97% rename from src/ImageSharp.Drawing/Processing/ImageBrush{TPixel}.cs rename to src/ImageSharp.Drawing/Processing/ImageBrush.cs index d5ec1123d7..983c09adbf 100644 --- a/src/ImageSharp.Drawing/Processing/ImageBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/ImageBrush.cs @@ -14,8 +14,7 @@ namespace SixLabors.ImageSharp.Processing /// /// Provides an implementation of an image brush for painting images within areas. /// - /// The pixel format. - public class ImageBrush : IBrush + public class ImageBrush : IBrush { /// /// The image to paint. @@ -23,7 +22,7 @@ namespace SixLabors.ImageSharp.Processing private readonly Image image; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The image. public ImageBrush(Image image) @@ -74,7 +73,7 @@ namespace SixLabors.ImageSharp.Processing private readonly int offsetX; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The target image. /// The image. diff --git a/src/ImageSharp.Drawing/Processing/LinearGradientBrush{TPixel}.cs b/src/ImageSharp.Drawing/Processing/LinearGradientBrush.cs similarity index 98% rename from src/ImageSharp.Drawing/Processing/LinearGradientBrush{TPixel}.cs rename to src/ImageSharp.Drawing/Processing/LinearGradientBrush.cs index 1a6f8d79ab..fac43ad634 100644 --- a/src/ImageSharp.Drawing/Processing/LinearGradientBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/LinearGradientBrush.cs @@ -13,7 +13,6 @@ namespace SixLabors.ImageSharp.Processing /// Supported right now: /// - a set of colors in relative distances to each other. /// - /// The pixel format public sealed class LinearGradientBrush : GradientBrushBase { diff --git a/src/ImageSharp.Drawing/Processing/PatternBrush{TPixel}.cs b/src/ImageSharp.Drawing/Processing/PatternBrush.cs similarity index 100% rename from src/ImageSharp.Drawing/Processing/PatternBrush{TPixel}.cs rename to src/ImageSharp.Drawing/Processing/PatternBrush.cs diff --git a/src/ImageSharp.Drawing/Processing/Pen{TPixel}.cs b/src/ImageSharp.Drawing/Processing/Pen.cs similarity index 88% rename from src/ImageSharp.Drawing/Processing/Pen{TPixel}.cs rename to src/ImageSharp.Drawing/Processing/Pen.cs index 922394946a..c9d60a50e2 100644 --- a/src/ImageSharp.Drawing/Processing/Pen{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/Pen.cs @@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Processing private readonly float[] pattern; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The color. /// The width. @@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Processing } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The brush. /// The width. @@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.Processing } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The color. /// The width. @@ -56,7 +56,7 @@ namespace SixLabors.ImageSharp.Processing } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The brush. /// The width. diff --git a/src/ImageSharp.Drawing/Processing/RadialGradientBrush{TPixel}.cs b/src/ImageSharp.Drawing/Processing/RadialGradientBrush.cs similarity index 100% rename from src/ImageSharp.Drawing/Processing/RadialGradientBrush{TPixel}.cs rename to src/ImageSharp.Drawing/Processing/RadialGradientBrush.cs diff --git a/src/ImageSharp.Drawing/Processing/RecolorBrush{TPixel}.cs b/src/ImageSharp.Drawing/Processing/RecolorBrush.cs similarity index 99% rename from src/ImageSharp.Drawing/Processing/RecolorBrush{TPixel}.cs rename to src/ImageSharp.Drawing/Processing/RecolorBrush.cs index ff098dce7c..fca95be327 100644 --- a/src/ImageSharp.Drawing/Processing/RecolorBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/RecolorBrush.cs @@ -15,7 +15,6 @@ namespace SixLabors.ImageSharp.Processing /// /// Provides an implementation of a brush that can recolor an image /// - /// The pixel format. public class RecolorBrush : IBrush { /// diff --git a/src/ImageSharp.Drawing/Processing/SolidBrush{TPixel}.cs b/src/ImageSharp.Drawing/Processing/SolidBrush.cs similarity index 100% rename from src/ImageSharp.Drawing/Processing/SolidBrush{TPixel}.cs rename to src/ImageSharp.Drawing/Processing/SolidBrush.cs diff --git a/tests/ImageSharp.Tests/Drawing/FillImageBrushTests.cs b/tests/ImageSharp.Tests/Drawing/FillImageBrushTests.cs index 772f62d5cc..e1fc7c30c1 100644 --- a/tests/ImageSharp.Tests/Drawing/FillImageBrushTests.cs +++ b/tests/ImageSharp.Tests/Drawing/FillImageBrushTests.cs @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing { using (var src = new Image(5, 5)) { - var brush = new ImageBrush(src); + var brush = new ImageBrush(src); using (var dest = new Image(10, 10)) { dest.Mutate(c => c.Fill(brush, new Rectangle(0, 0, 10, 10))); diff --git a/tests/ImageSharp.Tests/Drawing/FillPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/FillPolygonTests.cs index f84d2b31c9..5393e9954f 100644 --- a/tests/ImageSharp.Tests/Drawing/FillPolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/FillPolygonTests.cs @@ -95,7 +95,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing using (Image brushImage = Image.Load(TestFile.Create(brushImageName).Bytes)) { - var brush = new ImageBrush(brushImage); + var brush = new ImageBrush(brushImage); provider.RunValidatingProcessorTest( c => c.FillPolygon(brush, simplePath), From 659f8b62314a76f9d513e865b75b2e30ae0748cb Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 12 May 2019 22:54:38 +0200 Subject: [PATCH 186/223] ImageBrush can apply a source image of a different pixel type than the target --- .../Processing/ImageBrush.cs | 44 ++++++++++++++----- src/ImageSharp/Image.cs | 17 +++++++ src/ImageSharp/Image{TPixel}.cs | 11 +---- .../Drawing/DrawImageTests.cs | 2 + .../Drawing/FillImageBrushTests.cs | 19 ++++++++ .../TestUtilities/ImagingTestCaseUtility.cs | 6 +-- .../TestUtilities/TestImageExtensions.cs | 29 +++++------- tests/Images/External | 2 +- 8 files changed, 86 insertions(+), 44 deletions(-) diff --git a/src/ImageSharp.Drawing/Processing/ImageBrush.cs b/src/ImageSharp.Drawing/Processing/ImageBrush.cs index 983c09adbf..c2a8d3a261 100644 --- a/src/ImageSharp.Drawing/Processing/ImageBrush.cs +++ b/src/ImageSharp.Drawing/Processing/ImageBrush.cs @@ -3,6 +3,7 @@ using System; using System.Buffers; + using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -37,8 +38,14 @@ namespace SixLabors.ImageSharp.Processing GraphicsOptions options) where TPixel : struct, IPixel { - Image specificImage = (Image)this.image; - return new ImageBrushApplicator(source, specificImage.Frames.RootFrame, region, options); + if (this.image is Image specificImage) + { + return new ImageBrushApplicator(source, specificImage, region, options, false); + } + + specificImage = this.image.CloneAs(); + + return new ImageBrushApplicator(source, specificImage, region, options, true); } /// @@ -47,10 +54,11 @@ namespace SixLabors.ImageSharp.Processing private class ImageBrushApplicator : BrushApplicator where TPixel : struct, IPixel { - /// - /// The source image. - /// - private readonly ImageFrame source; + private ImageFrame sourceFrame; + + private Image sourceImage; + + private readonly bool shouldDisposeImage; /// /// The y-length. @@ -79,10 +87,18 @@ namespace SixLabors.ImageSharp.Processing /// The image. /// The region. /// The options - public ImageBrushApplicator(ImageFrame target, ImageFrame image, RectangleF region, GraphicsOptions options) + /// Whether to dispose the image on disposal of the applicator. + public ImageBrushApplicator( + ImageFrame target, + Image image, + RectangleF region, + GraphicsOptions options, + bool shouldDisposeImage) : base(target, options) { - this.source = image; + this.sourceImage = image; + this.sourceFrame = image.Frames.RootFrame; + this.shouldDisposeImage = shouldDisposeImage; this.xLength = image.Width; this.yLength = image.Height; this.offsetY = (int)MathF.Max(MathF.Floor(region.Top), 0); @@ -103,13 +119,19 @@ namespace SixLabors.ImageSharp.Processing { int srcX = (x - this.offsetX) % this.xLength; int srcY = (y - this.offsetY) % this.yLength; - return this.source[srcX, srcY]; + return this.sourceFrame[srcX, srcY]; } } /// public override void Dispose() { + if (this.shouldDisposeImage) + { + this.sourceImage?.Dispose(); + this.sourceImage = null; + this.sourceFrame = null; + } } /// @@ -124,7 +146,7 @@ namespace SixLabors.ImageSharp.Processing int sourceY = (y - this.offsetY) % this.yLength; int offsetX = x - this.offsetX; - Span sourceRow = this.source.GetPixelRowSpan(sourceY); + Span sourceRow = this.sourceFrame.GetPixelRowSpan(sourceY); for (int i = 0; i < scanline.Length; i++) { @@ -137,7 +159,7 @@ namespace SixLabors.ImageSharp.Processing Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); this.Blender.Blend( - this.source.Configuration, + this.sourceFrame.Configuration, destinationRow, destinationRow, overlaySpan, diff --git a/src/ImageSharp/Image.cs b/src/ImageSharp/Image.cs index 1566fd0eed..db03281064 100644 --- a/src/ImageSharp/Image.cs +++ b/src/ImageSharp/Image.cs @@ -87,6 +87,23 @@ namespace SixLabors.ImageSharp EncodeVisitor visitor = new EncodeVisitor(encoder, stream); this.AcceptVisitor(visitor); } + + /// + /// Returns a copy of the image in the given pixel format. + /// + /// The pixel format. + /// The + public Image CloneAs() + where TPixel2 : struct, IPixel => this.CloneAs(this.Configuration); + + /// + /// Returns a copy of the image in the given pixel format. + /// + /// The pixel format. + /// The configuration providing initialization code which allows extending the library. + /// The . + public abstract Image CloneAs(Configuration configuration) + where TPixel2 : struct, IPixel; /// /// Accept a . diff --git a/src/ImageSharp/Image{TPixel}.cs b/src/ImageSharp/Image{TPixel}.cs index 27e2bc593c..2b93b710b0 100644 --- a/src/ImageSharp/Image{TPixel}.cs +++ b/src/ImageSharp/Image{TPixel}.cs @@ -153,22 +153,13 @@ namespace SixLabors.ImageSharp return new Image(configuration, this.Metadata.DeepClone(), clonedFrames); } - /// - /// Returns a copy of the image in the given pixel format. - /// - /// The pixel format. - /// The - public Image CloneAs() - where TPixel2 : struct, IPixel => this.CloneAs(this.Configuration); - /// /// Returns a copy of the image in the given pixel format. /// /// The pixel format. /// The configuration providing initialization code which allows extending the library. /// The . - public Image CloneAs(Configuration configuration) - where TPixel2 : struct, IPixel + public override Image CloneAs(Configuration configuration) { IEnumerable> clonedFrames = this.Frames.Select(x => x.CloneAs(configuration)); return new Image(configuration, this.Metadata.DeepClone(), clonedFrames); diff --git a/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs b/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs index 70879e6feb..e48a0b0ae3 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs @@ -175,5 +175,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing } } } + + } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Drawing/FillImageBrushTests.cs b/tests/ImageSharp.Tests/Drawing/FillImageBrushTests.cs index e1fc7c30c1..d33d7b70d2 100644 --- a/tests/ImageSharp.Tests/Drawing/FillImageBrushTests.cs +++ b/tests/ImageSharp.Tests/Drawing/FillImageBrushTests.cs @@ -32,5 +32,24 @@ namespace SixLabors.ImageSharp.Tests.Drawing } } } + + [Theory] + [WithTestPatternImages(200, 200, PixelTypes.Rgba32 | PixelTypes.Bgra32)] + public void UseBrushOfDifferentPixelType(TestImageProvider provider) + where TPixel : struct, IPixel + { + byte[] data = TestFile.Create(TestImages.Png.Ducky).Bytes; + using (Image background = provider.GetImage()) + using (Image overlay = provider.PixelType == PixelTypes.Rgba32 + ? (Image)Image.Load(data) + : Image.Load(data)) + { + var brush = new ImageBrush(overlay); + background.Mutate(c => c.Fill(brush)); + + background.DebugSave(provider, appendSourceFileOrDescription : false); + background.CompareToReferenceOutput(provider, appendSourceFileOrDescription: false); + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs index c91ef56a1a..92cc9f6368 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs @@ -152,21 +152,19 @@ namespace SixLabors.ImageSharp.Tests /// /// Encodes image by the format matching the required extension, than saves it to the recommended output file. /// - /// The pixel format of the image /// The image instance /// The requested extension /// Optional encoder /// A value indicating whether to append the pixel type to the test output file name /// A boolean indicating whether to append to the test output file name. /// Additional information to append to the test output file name - public string SaveTestOutputFile( - Image image, + public string SaveTestOutputFile( + Image image, string extension = null, IImageEncoder encoder = null, object testOutputDetails = null, bool appendPixelTypeToFileName = true, bool appendSourceFileOrDescription = true) - where TPixel : struct, IPixel { string path = this.GetTestOutputFileName( extension, diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index 048c6347cd..e77b1974d3 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -55,17 +55,16 @@ namespace SixLabors.ImageSharp.Tests }); } - public static Image DebugSave( - this Image image, + public static void DebugSave( + this Image image, ITestImageProvider provider, FormattableString testOutputDetails, string extension = "png", bool appendPixelTypeToFileName = true, bool appendSourceFileOrDescription = true, IImageEncoder encoder = null) - where TPixel : struct, IPixel { - return image.DebugSave( + image.DebugSave( provider, (object)testOutputDetails, extension, @@ -77,7 +76,6 @@ namespace SixLabors.ImageSharp.Tests /// /// Saves the image only when not running in the CI server. /// - /// The pixel format /// The image /// The image provider /// Details to be concatenated to the test output file, describing the parameters of the test. @@ -85,15 +83,14 @@ namespace SixLabors.ImageSharp.Tests /// A boolean indicating whether to append the pixel type to the output file name. /// A boolean indicating whether to append to the test output file name. /// Custom encoder to use. - public static Image DebugSave( - this Image image, + public static Image DebugSave( + this Image image, ITestImageProvider provider, object testOutputDetails = null, string extension = "png", bool appendPixelTypeToFileName = true, bool appendSourceFileOrDescription = true, IImageEncoder encoder = null) - where TPixel : struct, IPixel { if (TestEnvironment.RunsOnCI) { @@ -111,37 +108,34 @@ namespace SixLabors.ImageSharp.Tests return image; } - public static Image DebugSave( - this Image image, + public static void DebugSave( + this Image image, ITestImageProvider provider, IImageEncoder encoder, FormattableString testOutputDetails, bool appendPixelTypeToFileName = true) - where TPixel : struct, IPixel { - return image.DebugSave(provider, encoder, (object)testOutputDetails, appendPixelTypeToFileName); + image.DebugSave(provider, encoder, (object)testOutputDetails, appendPixelTypeToFileName); } /// /// Saves the image only when not running in the CI server. /// - /// The pixel format /// The image /// The image provider /// The image encoder /// Details to be concatenated to the test output file, describing the parameters of the test. /// A boolean indicating whether to append the pixel type to the output file name. - public static Image DebugSave( - this Image image, + public static void DebugSave( + this Image image, ITestImageProvider provider, IImageEncoder encoder, object testOutputDetails = null, bool appendPixelTypeToFileName = true) - where TPixel : struct, IPixel { if (TestEnvironment.RunsOnCI) { - return image; + return; } // We are running locally then we want to save it out @@ -150,7 +144,6 @@ namespace SixLabors.ImageSharp.Tests encoder: encoder, testOutputDetails: testOutputDetails, appendPixelTypeToFileName: appendPixelTypeToFileName); - return image; } public static Image DebugSaveMultiFrame( diff --git a/tests/Images/External b/tests/Images/External index 55c250ab80..9d1d9dd537 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 55c250ab80f7f9fc26208b9b9d9590cbe8012446 +Subproject commit 9d1d9dd53755007ee812907f64304d1925c6a91a From 0f823b9b4a6fe43b932f073900b106c899b9d587 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 13 May 2019 01:56:07 +0200 Subject: [PATCH 187/223] non-generic DrawImageProcessor --- .../Extensions/DrawImageExtensions.cs | 134 +++++++++++------- .../Processors/Drawing/DrawImageProcessor.cs | 117 ++++++++++++--- 2 files changed, 187 insertions(+), 64 deletions(-) diff --git a/src/ImageSharp.Drawing/Processing/Extensions/DrawImageExtensions.cs b/src/ImageSharp.Drawing/Processing/Extensions/DrawImageExtensions.cs index 58459ebc5d..981cf1bef4 100644 --- a/src/ImageSharp.Drawing/Processing/Extensions/DrawImageExtensions.cs +++ b/src/ImageSharp.Drawing/Processing/Extensions/DrawImageExtensions.cs @@ -15,98 +15,126 @@ namespace SixLabors.ImageSharp.Processing /// /// Draws the given image together with the current one by blending their pixels. /// - /// The pixel format of the destination image. - /// The pixel format of the source image. /// The image this method extends. /// The image to blend with the currently processing image. /// The opacity of the image to blend. Must be between 0 and 1. /// The . - public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, float opacity) - where TPixelDst : struct, IPixel - where TPixelSrc : struct, IPixel - => source.ApplyProcessor(new DrawImageProcessor(image, Point.Empty, GraphicsOptions.Default.ColorBlendingMode, GraphicsOptions.Default.AlphaCompositionMode, opacity)); + public static IImageProcessingContext DrawImage( + this IImageProcessingContext source, + Image image, + float opacity) => + source.ApplyProcessor( + new DrawImageProcessor( + image, + Point.Empty, + GraphicsOptions.Default.ColorBlendingMode, + GraphicsOptions.Default.AlphaCompositionMode, + opacity)); /// /// Draws the given image together with the current one by blending their pixels. /// - /// The pixel format of the destination image. - /// The pixel format of the source image. /// The image this method extends. /// The image to blend with the currently processing image. /// The blending mode. /// The opacity of the image to blend. Must be between 0 and 1. /// The . - public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, PixelColorBlendingMode colorBlending, float opacity) - where TPixelDst : struct, IPixel - where TPixelSrc : struct, IPixel - => source.ApplyProcessor(new DrawImageProcessor(image, Point.Empty, colorBlending, GraphicsOptions.Default.AlphaCompositionMode, opacity)); + public static IImageProcessingContext DrawImage( + this IImageProcessingContext source, + Image image, + PixelColorBlendingMode colorBlending, + float opacity) => + source.ApplyProcessor( + new DrawImageProcessor( + image, + Point.Empty, + colorBlending, + GraphicsOptions.Default.AlphaCompositionMode, + opacity)); /// /// Draws the given image together with the current one by blending their pixels. /// - /// The pixel format of the destination image. - /// The pixel format of the source image. /// The image this method extends. /// The image to blend with the currently processing image. /// The color blending mode. /// The alpha composition mode. /// The opacity of the image to blend. Must be between 0 and 1. /// The . - public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, float opacity) - where TPixelDst : struct, IPixel - where TPixelSrc : struct, IPixel - => source.ApplyProcessor(new DrawImageProcessor(image, Point.Empty, colorBlending, alphaComposition, opacity)); + public static IImageProcessingContext DrawImage( + this IImageProcessingContext source, + Image image, + PixelColorBlendingMode colorBlending, + PixelAlphaCompositionMode alphaComposition, + float opacity) => + source.ApplyProcessor(new DrawImageProcessor(image, Point.Empty, colorBlending, alphaComposition, opacity)); /// /// Draws the given image together with the current one by blending their pixels. /// - /// The pixel format of the destination image. - /// The pixel format of the source image. /// The image this method extends. /// The image to blend with the currently processing image. /// The options, including the blending type and blending amount. /// The . - public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, GraphicsOptions options) - where TPixelDst : struct, IPixel - where TPixelSrc : struct, IPixel - => source.ApplyProcessor(new DrawImageProcessor(image, Point.Empty, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage)); + public static IImageProcessingContext DrawImage( + this IImageProcessingContext source, + Image image, + GraphicsOptions options) => + source.ApplyProcessor( + new DrawImageProcessor( + image, + Point.Empty, + options.ColorBlendingMode, + options.AlphaCompositionMode, + options.BlendPercentage)); /// /// Draws the given image together with the current one by blending their pixels. /// - /// The pixel format of the destination image. - /// The pixel format of the source image. /// The image this method extends. /// The image to blend with the currently processing image. /// The location to draw the blended image. /// The opacity of the image to blend. Must be between 0 and 1. /// The . - public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, Point location, float opacity) - where TPixelDst : struct, IPixel - where TPixelSrc : struct, IPixel - => source.ApplyProcessor(new DrawImageProcessor(image, location, GraphicsOptions.Default.ColorBlendingMode, GraphicsOptions.Default.AlphaCompositionMode, opacity)); + public static IImageProcessingContext DrawImage( + this IImageProcessingContext source, + Image image, + Point location, + float opacity) => + source.ApplyProcessor( + new DrawImageProcessor( + image, + location, + GraphicsOptions.Default.ColorBlendingMode, + GraphicsOptions.Default.AlphaCompositionMode, + opacity)); /// /// Draws the given image together with the current one by blending their pixels. /// - /// The pixel format of the destination image. - /// The pixel format of the source image. /// The image this method extends. /// The image to blend with the currently processing image. /// The location to draw the blended image. /// The color blending to apply. /// The opacity of the image to blend. Must be between 0 and 1. /// The . - public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, Point location, PixelColorBlendingMode colorBlending, float opacity) - where TPixelDst : struct, IPixel - where TPixelSrc : struct, IPixel - => source.ApplyProcessor(new DrawImageProcessor(image, location, colorBlending, GraphicsOptions.Default.AlphaCompositionMode, opacity)); + public static IImageProcessingContext DrawImage( + this IImageProcessingContext source, + Image image, + Point location, + PixelColorBlendingMode colorBlending, + float opacity) => + source.ApplyProcessor( + new DrawImageProcessor( + image, + location, + colorBlending, + GraphicsOptions.Default.AlphaCompositionMode, + opacity)); /// /// Draws the given image together with the current one by blending their pixels. /// - /// The pixel format of the destination image. - /// The pixel format of the source image. /// The image this method extends. /// The image to blend with the currently processing image. /// The location to draw the blended image. @@ -114,24 +142,34 @@ namespace SixLabors.ImageSharp.Processing /// The alpha composition mode. /// The opacity of the image to blend. Must be between 0 and 1. /// The . - public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, Point location, PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, float opacity) - where TPixelDst : struct, IPixel - where TPixelSrc : struct, IPixel - => source.ApplyProcessor(new DrawImageProcessor(image, location, colorBlending, alphaComposition, opacity)); + public static IImageProcessingContext DrawImage( + this IImageProcessingContext source, + Image image, + Point location, + PixelColorBlendingMode colorBlending, + PixelAlphaCompositionMode alphaComposition, + float opacity) => + source.ApplyProcessor(new DrawImageProcessor(image, location, colorBlending, alphaComposition, opacity)); /// /// Draws the given image together with the current one by blending their pixels. /// - /// The pixel format of the destination image. - /// The pixel format of the source image. /// The image this method extends. /// The image to blend with the currently processing image. /// The location to draw the blended image. /// The options containing the blend mode and opacity. /// The . - public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, Point location, GraphicsOptions options) - where TPixelDst : struct, IPixel - where TPixelSrc : struct, IPixel - => source.ApplyProcessor(new DrawImageProcessor(image, location, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage)); + public static IImageProcessingContext DrawImage( + this IImageProcessingContext source, + Image image, + Point location, + GraphicsOptions options) => + source.ApplyProcessor( + new DrawImageProcessor( + image, + location, + options.ColorBlendingMode, + options.AlphaCompositionMode, + options.BlendPercentage)); } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs index 6c03537eb3..0fbef9afea 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs @@ -13,14 +13,90 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Drawing { + public class DrawImageProcessor : IImageProcessor + { + public DrawImageProcessor( + Image image, + Point location, + PixelColorBlendingMode colorBlendingMode, + PixelAlphaCompositionMode alphaCompositionMode, + float opacity) + { + this.Image = image; + this.Location = location; + this.ColorBlendingMode = colorBlendingMode; + this.AlphaCompositionMode = alphaCompositionMode; + this.Opacity = opacity; + } + + /// + /// Gets the image to blend + /// + public Image Image { get; } + + /// + /// Gets the location to draw the blended image + /// + public Point Location { get; } + + /// + /// Gets the blending mode to use when drawing the image. + /// + public PixelColorBlendingMode ColorBlendingMode { get; } + + /// + /// Gets the Alpha blending mode to use when drawing the image. + /// + public PixelAlphaCompositionMode AlphaCompositionMode { get; } + + /// + /// Gets the opacity of the image to blend + /// + public float Opacity { get; } + + /// + public IImageProcessor CreatePixelSpecificProcessor() + where TPixelBg : struct, IPixel + { + var visitor = new ProcessorFactoryVisitor(this); + this.Image.AcceptVisitor(visitor); + return visitor.Result; + } + + private class ProcessorFactoryVisitor : IImageVisitor + where TPixelBg : struct, IPixel + { + private readonly DrawImageProcessor definition; + + public ProcessorFactoryVisitor(DrawImageProcessor definition) + { + this.definition = definition; + } + + public IImageProcessor Result { get; private set; } + + public void Visit(Image image) + where TPixelFg : struct, IPixel + { + this.Result = new DrawImageProcessor( + image, + this.definition.Location, + this.definition.ColorBlendingMode, + this.definition.AlphaCompositionMode, + this.definition.Opacity + ); + } + } + } + /// /// Combines two images together by blending the pixels. /// - /// The pixel format of destination image. - /// The pixel format of source image. - internal class DrawImageProcessor : ImageProcessor - where TPixelDst : struct, IPixel - where TPixelSrc : struct, IPixel + /// The pixel format of destination image. + /// The pixel format of source image. + internal class DrawImageProcessor : ImageProcessor + where TPixelBg : struct, IPixel + where TPixelFg : struct, IPixel { /// /// Initializes a new instance of the class. @@ -30,20 +106,25 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing /// The blending mode to use when drawing the image. /// The Alpha blending mode to use when drawing the image. /// The opacity of the image to blend. Must be between 0 and 1. - public DrawImageProcessor(Image image, Point location, PixelColorBlendingMode colorBlendingMode, PixelAlphaCompositionMode alphaCompositionMode, float opacity) + public DrawImageProcessor( + Image image, + Point location, + PixelColorBlendingMode colorBlendingMode, + PixelAlphaCompositionMode alphaCompositionMode, + float opacity) { Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity)); this.Image = image; this.Opacity = opacity; - this.Blender = PixelOperations.Instance.GetPixelBlender(colorBlendingMode, alphaCompositionMode); + this.Blender = PixelOperations.Instance.GetPixelBlender(colorBlendingMode, alphaCompositionMode); this.Location = location; } /// /// Gets the image to blend /// - public Image Image { get; } + public Image Image { get; } /// /// Gets the opacity of the image to blend @@ -53,7 +134,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing /// /// Gets the pixel blender /// - public PixelBlender Blender { get; } + public PixelBlender Blender { get; } /// /// Gets the location to draw the blended image @@ -61,10 +142,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing public Point Location { get; } /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + protected override void OnFrameApply( + ImageFrame source, + Rectangle sourceRectangle, + Configuration configuration) { - Image targetImage = this.Image; - PixelBlender blender = this.Blender; + Image targetImage = this.Image; + PixelBlender blender = this.Blender; int locationY = this.Location.Y; // Align start/end positions. @@ -84,7 +168,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing // not a valid operation because rectangle does not overlap with this image. if (workingRect.Width <= 0 || workingRect.Height <= 0) { - throw new ImageProcessingException("Cannot draw image because the source image does not overlap the target image."); + throw new ImageProcessingException( + "Cannot draw image because the source image does not overlap the target image."); } ParallelHelper.IterateRows( @@ -94,10 +179,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing { for (int y = rows.Min; y < rows.Max; y++) { - Span background = source.GetPixelRowSpan(y).Slice(minX, width); - Span foreground = + Span background = source.GetPixelRowSpan(y).Slice(minX, width); + Span foreground = targetImage.GetPixelRowSpan(y - locationY).Slice(targetX, width); - blender.Blend(configuration, background, background, foreground, this.Opacity); + blender.Blend(configuration, background, background, foreground, this.Opacity); } }); } From 79439dbea1754a2b661ade7b786a1283812932d4 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 13 May 2019 02:13:16 +0200 Subject: [PATCH 188/223] DrawImageOfDifferentPixelType test cases --- .../Drawing/DrawImageTests.cs | 22 +++++++++++++++++-- tests/Images/External | 2 +- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs b/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs index e48a0b0ae3..cb44651d6f 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs @@ -96,8 +96,26 @@ namespace SixLabors.ImageSharp.Tests.Drawing testInfo); } } - - + + [Theory] + [WithTestPatternImages(200, 200, PixelTypes.Rgba32 | PixelTypes.Bgra32)] + public void DrawImageOfDifferentPixelType(TestImageProvider provider) + where TPixel : struct, IPixel + { + byte[] brushData = TestFile.Create(TestImages.Png.Ducky).Bytes; + + using (Image image = provider.GetImage()) + using (Image brushImage = provider.PixelType == PixelTypes.Rgba32 + ? (Image)Image.Load(brushData) + : Image.Load(brushData)) + { + image.Mutate(c => c.DrawImage(brushImage, 0.5f)); + + image.DebugSave(provider, appendSourceFileOrDescription: false); + image.CompareToReferenceOutput(provider, appendSourceFileOrDescription: false); + } + } + [Theory] [WithSolidFilledImages(100, 100, "White", PixelTypes.Rgba32, 0, 0)] [WithSolidFilledImages(100, 100, "White", PixelTypes.Rgba32, 25, 25)] diff --git a/tests/Images/External b/tests/Images/External index 9d1d9dd537..82f082ed3b 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 9d1d9dd53755007ee812907f64304d1925c6a91a +Subproject commit 82f082ed3b47cc21b38dda9c5f8dda06dca5233a From 17a4d91b825b60b91fdfa4655ecb33c4c5974422 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 13 May 2019 23:52:03 +0200 Subject: [PATCH 189/223] clean-up drawing processors --- .../ImageSharp.Drawing.csproj | 2 +- .../Processing/BrushApplicator.cs | 2 +- .../Processing/PatternBrush.cs | 2 +- .../Processors/Drawing/DrawImageProcessor.cs | 131 +---- .../DrawImageProcessor{TPixelBg,TPixelFg}.cs | 111 +++++ .../Processors/Drawing/FillProcessor.cs | 128 +---- .../Drawing/FillProcessor{TPixel}.cs | 118 +++++ .../Processors/Drawing/FillRegionProcessor.cs | 197 +------- .../Drawing/FillRegionProcessor{TPixel}.cs | 195 ++++++++ .../Processors/Text/DrawTextProcessor.cs | 445 +---------------- .../Text/DrawTextProcessor{TPixel}.cs | 446 ++++++++++++++++++ .../Processing/RadialGradientBrush.cs | 1 - .../Processing/SolidBrush.cs | 4 +- .../Processing/TextGraphicsOptions.cs | 328 +++++++------ src/ImageSharp/Image.cs | 2 +- src/ImageSharp/ImageSharp.csproj | 2 +- 16 files changed, 1088 insertions(+), 1026 deletions(-) create mode 100644 src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs create mode 100644 src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor{TPixel}.cs create mode 100644 src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor{TPixel}.cs create mode 100644 src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor{TPixel}.cs diff --git a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj index fdeb27a770..722ef33ff0 100644 --- a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj +++ b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj @@ -40,9 +40,9 @@ + - diff --git a/src/ImageSharp.Drawing/Processing/BrushApplicator.cs b/src/ImageSharp.Drawing/Processing/BrushApplicator.cs index 0c6e0d3b40..7e75d7effc 100644 --- a/src/ImageSharp.Drawing/Processing/BrushApplicator.cs +++ b/src/ImageSharp.Drawing/Processing/BrushApplicator.cs @@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Processing /// /// Gets the blend percentage /// - protected GraphicsOptions Options { get; private set; } + protected GraphicsOptions Options { get; } /// /// Gets the color for a single pixel. diff --git a/src/ImageSharp.Drawing/Processing/PatternBrush.cs b/src/ImageSharp.Drawing/Processing/PatternBrush.cs index 21690055e1..a7a6785b92 100644 --- a/src/ImageSharp.Drawing/Processing/PatternBrush.cs +++ b/src/ImageSharp.Drawing/Processing/PatternBrush.cs @@ -81,7 +81,7 @@ namespace SixLabors.ImageSharp.Processing } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The brush. internal PatternBrush(PatternBrush brush) diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs index 0fbef9afea..54b7315b21 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs @@ -1,20 +1,29 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; using System.Buffers; using System.Threading.Tasks; -using SixLabors.ImageSharp.Advanced; + using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Memory; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Drawing { + /// + /// Combines two images together by blending the pixels. + /// public class DrawImageProcessor : IImageProcessor { + /// + /// Initializes a new instance of the class. + /// + /// The image to blend. + /// The location to draw the blended image. + /// The blending mode to use when drawing the image. + /// The Alpha blending mode to use when drawing the image. + /// The opacity of the image to blend. public DrawImageProcessor( Image image, Point location, @@ -30,27 +39,27 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing } /// - /// Gets the image to blend + /// Gets the image to blend. /// public Image Image { get; } /// - /// Gets the location to draw the blended image + /// Gets the location to draw the blended image. /// public Point Location { get; } - + /// /// Gets the blending mode to use when drawing the image. /// public PixelColorBlendingMode ColorBlendingMode { get; } - + /// /// Gets the Alpha blending mode to use when drawing the image. /// public PixelAlphaCompositionMode AlphaCompositionMode { get; } - + /// - /// Gets the opacity of the image to blend + /// Gets the opacity of the image to blend. /// public float Opacity { get; } @@ -74,7 +83,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing } public IImageProcessor Result { get; private set; } - + public void Visit(Image image) where TPixelFg : struct, IPixel { @@ -83,108 +92,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing this.definition.Location, this.definition.ColorBlendingMode, this.definition.AlphaCompositionMode, - this.definition.Opacity - ); + this.definition.Opacity); } } } - - /// - /// Combines two images together by blending the pixels. - /// - /// The pixel format of destination image. - /// The pixel format of source image. - internal class DrawImageProcessor : ImageProcessor - where TPixelBg : struct, IPixel - where TPixelFg : struct, IPixel - { - /// - /// Initializes a new instance of the class. - /// - /// The image to blend with the currently processing image. - /// The location to draw the blended image. - /// The blending mode to use when drawing the image. - /// The Alpha blending mode to use when drawing the image. - /// The opacity of the image to blend. Must be between 0 and 1. - public DrawImageProcessor( - Image image, - Point location, - PixelColorBlendingMode colorBlendingMode, - PixelAlphaCompositionMode alphaCompositionMode, - float opacity) - { - Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity)); - - this.Image = image; - this.Opacity = opacity; - this.Blender = PixelOperations.Instance.GetPixelBlender(colorBlendingMode, alphaCompositionMode); - this.Location = location; - } - - /// - /// Gets the image to blend - /// - public Image Image { get; } - - /// - /// Gets the opacity of the image to blend - /// - public float Opacity { get; } - - /// - /// Gets the pixel blender - /// - public PixelBlender Blender { get; } - - /// - /// Gets the location to draw the blended image - /// - public Point Location { get; } - - /// - protected override void OnFrameApply( - ImageFrame source, - Rectangle sourceRectangle, - Configuration configuration) - { - Image targetImage = this.Image; - PixelBlender blender = this.Blender; - int locationY = this.Location.Y; - - // Align start/end positions. - Rectangle bounds = targetImage.Bounds(); - - int minX = Math.Max(this.Location.X, sourceRectangle.X); - int maxX = Math.Min(this.Location.X + bounds.Width, sourceRectangle.Right); - int targetX = minX - this.Location.X; - - int minY = Math.Max(this.Location.Y, sourceRectangle.Y); - int maxY = Math.Min(this.Location.Y + bounds.Height, sourceRectangle.Bottom); - - int width = maxX - minX; - - var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY); - - // not a valid operation because rectangle does not overlap with this image. - if (workingRect.Width <= 0 || workingRect.Height <= 0) - { - throw new ImageProcessingException( - "Cannot draw image because the source image does not overlap the target image."); - } - - ParallelHelper.IterateRows( - workingRect, - configuration, - rows => - { - for (int y = rows.Min; y < rows.Max; y++) - { - Span background = source.GetPixelRowSpan(y).Slice(minX, width); - Span foreground = - targetImage.GetPixelRowSpan(y - locationY).Slice(targetX, width); - blender.Blend(configuration, background, background, foreground, this.Opacity); - } - }); - } - } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs new file mode 100644 index 0000000000..21599bf787 --- /dev/null +++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs @@ -0,0 +1,111 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Drawing +{ + /// + /// Combines two images together by blending the pixels. + /// + /// The pixel format of destination image. + /// The pixel format of source image. + internal class DrawImageProcessor : ImageProcessor + where TPixelBg : struct, IPixel + where TPixelFg : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + /// The image to blend with the currently processing image. + /// The location to draw the blended image. + /// The blending mode to use when drawing the image. + /// The Alpha blending mode to use when drawing the image. + /// The opacity of the image to blend. Must be between 0 and 1. + public DrawImageProcessor( + Image image, + Point location, + PixelColorBlendingMode colorBlendingMode, + PixelAlphaCompositionMode alphaCompositionMode, + float opacity) + { + Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity)); + + this.Image = image; + this.Opacity = opacity; + this.Blender = PixelOperations.Instance.GetPixelBlender(colorBlendingMode, alphaCompositionMode); + this.Location = location; + } + + /// + /// Gets the image to blend + /// + public Image Image { get; } + + /// + /// Gets the opacity of the image to blend + /// + public float Opacity { get; } + + /// + /// Gets the pixel blender + /// + public PixelBlender Blender { get; } + + /// + /// Gets the location to draw the blended image + /// + public Point Location { get; } + + /// + protected override void OnFrameApply( + ImageFrame source, + Rectangle sourceRectangle, + Configuration configuration) + { + Image targetImage = this.Image; + PixelBlender blender = this.Blender; + int locationY = this.Location.Y; + + // Align start/end positions. + Rectangle bounds = targetImage.Bounds(); + + int minX = Math.Max(this.Location.X, sourceRectangle.X); + int maxX = Math.Min(this.Location.X + bounds.Width, sourceRectangle.Right); + int targetX = minX - this.Location.X; + + int minY = Math.Max(this.Location.Y, sourceRectangle.Y); + int maxY = Math.Min(this.Location.Y + bounds.Height, sourceRectangle.Bottom); + + int width = maxX - minX; + + var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY); + + // not a valid operation because rectangle does not overlap with this image. + if (workingRect.Width <= 0 || workingRect.Height <= 0) + { + throw new ImageProcessingException( + "Cannot draw image because the source image does not overlap the target image."); + } + + ParallelHelper.IterateRows( + workingRect, + configuration, + rows => + { + for (int y = rows.Min; y < rows.Max; y++) + { + Span background = source.GetPixelRowSpan(y).Slice(minX, width); + Span foreground = + targetImage.GetPixelRowSpan(y - locationY).Slice(targetX, width); + blender.Blend(configuration, background, background, foreground, this.Opacity); + } + }); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs index 8ea1c48cc2..d6254c7cfb 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs @@ -1,28 +1,38 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; -using System.Buffers; using System.Threading.Tasks; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; + using SixLabors.ImageSharp.PixelFormats; using SixLabors.Memory; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Drawing { + /// + /// Defines a processor to fill an with the given + /// using blending defined by the given . + /// public class FillProcessor : IImageProcessor { + /// + /// Initializes a new instance of the class. + /// + /// The brush to use for filling. + /// The defining how to blend the brush pixels over the image pixels. public FillProcessor(IBrush brush, GraphicsOptions options) { this.Brush = brush; this.Options = options; } + /// + /// Gets the used for filling the destination image. + /// public IBrush Brush { get; } - + + /// + /// Gets the defining how to blend the brush pixels over the image pixels. + /// public GraphicsOptions Options { get; } /// @@ -32,108 +42,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing return new FillProcessor(this); } } - - /// - /// Using the brush as a source of pixels colors blends the brush color with source. - /// - /// The pixel format. - internal class FillProcessor : ImageProcessor - where TPixel : struct, IPixel - { - private readonly FillProcessor definition; - - public FillProcessor(FillProcessor definition) - { - this.definition = definition; - } - - /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - { - int startX = sourceRectangle.X; - int endX = sourceRectangle.Right; - int startY = sourceRectangle.Y; - int endY = sourceRectangle.Bottom; - - // Align start/end positions. - int minX = Math.Max(0, startX); - int maxX = Math.Min(source.Width, endX); - int minY = Math.Max(0, startY); - int maxY = Math.Min(source.Height, endY); - - int width = maxX - minX; - - var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY); - - IBrush brush = this.definition.Brush; - GraphicsOptions options = this.definition.Options; - - // If there's no reason for blending, then avoid it. - if (this.IsSolidBrushWithoutBlending(out SolidBrush solidBrush)) - { - ParallelExecutionSettings parallelSettings = configuration.GetParallelSettings().MultiplyMinimumPixelsPerTask(4); - - TPixel colorPixel = solidBrush.Color.ToPixel(); - - ParallelHelper.IterateRows( - workingRect, - parallelSettings, - rows => - { - for (int y = rows.Min; y < rows.Max; y++) - { - source.GetPixelRowSpan(y).Slice(minX, width).Fill(colorPixel); - } - }); - } - else - { - // Reset offset if necessary. - if (minX > 0) - { - startX = 0; - } - - if (minY > 0) - { - startY = 0; - } - - using (IMemoryOwner amount = source.MemoryAllocator.Allocate(width)) - using (BrushApplicator applicator = brush.CreateApplicator( - source, - sourceRectangle, - options)) - { - amount.GetSpan().Fill(1f); - - ParallelHelper.IterateRows( - workingRect, - configuration, - rows => - { - for (int y = rows.Min; y < rows.Max; y++) - { - int offsetY = y - startY; - int offsetX = minX - startX; - - applicator.Apply(amount.GetSpan(), offsetX, offsetY); - } - }); - } - } - } - - private bool IsSolidBrushWithoutBlending(out SolidBrush solidBrush) - { - solidBrush = this.definition.Brush as SolidBrush; - - if (solidBrush == null) - { - return false; - } - - return this.definition.Options.IsOpaqueColorWithoutBlending(solidBrush.Color); - } - } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor{TPixel}.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor{TPixel}.cs new file mode 100644 index 0000000000..68aef82e2d --- /dev/null +++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor{TPixel}.cs @@ -0,0 +1,118 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Drawing +{ + /// + /// Using the brush as a source of pixels colors blends the brush color with source. + /// + /// The pixel format. + internal class FillProcessor : ImageProcessor + where TPixel : struct, IPixel + { + private readonly FillProcessor definition; + + public FillProcessor(FillProcessor definition) + { + this.definition = definition; + } + + /// + protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + int startX = sourceRectangle.X; + int endX = sourceRectangle.Right; + int startY = sourceRectangle.Y; + int endY = sourceRectangle.Bottom; + + // Align start/end positions. + int minX = Math.Max(0, startX); + int maxX = Math.Min(source.Width, endX); + int minY = Math.Max(0, startY); + int maxY = Math.Min(source.Height, endY); + + int width = maxX - minX; + + var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY); + + IBrush brush = this.definition.Brush; + GraphicsOptions options = this.definition.Options; + + // If there's no reason for blending, then avoid it. + if (this.IsSolidBrushWithoutBlending(out SolidBrush solidBrush)) + { + ParallelExecutionSettings parallelSettings = configuration.GetParallelSettings().MultiplyMinimumPixelsPerTask(4); + + TPixel colorPixel = solidBrush.Color.ToPixel(); + + ParallelHelper.IterateRows( + workingRect, + parallelSettings, + rows => + { + for (int y = rows.Min; y < rows.Max; y++) + { + source.GetPixelRowSpan(y).Slice(minX, width).Fill(colorPixel); + } + }); + } + else + { + // Reset offset if necessary. + if (minX > 0) + { + startX = 0; + } + + if (minY > 0) + { + startY = 0; + } + + using (IMemoryOwner amount = source.MemoryAllocator.Allocate(width)) + using (BrushApplicator applicator = brush.CreateApplicator( + source, + sourceRectangle, + options)) + { + amount.GetSpan().Fill(1f); + + ParallelHelper.IterateRows( + workingRect, + configuration, + rows => + { + for (int y = rows.Min; y < rows.Max; y++) + { + int offsetY = y - startY; + int offsetX = minX - startX; + + applicator.Apply(amount.GetSpan(), offsetX, offsetY); + } + }); + } + } + } + + private bool IsSolidBrushWithoutBlending(out SolidBrush solidBrush) + { + solidBrush = this.definition.Brush as SolidBrush; + + if (solidBrush == null) + { + return false; + } + + return this.definition.Options.IsOpaqueColorWithoutBlending(solidBrush.Color); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs index 3653102efa..1b207d4cd4 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs @@ -1,18 +1,16 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; -using System.Buffers; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; -using SixLabors.ImageSharp.Utils; using SixLabors.Memory; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Drawing { + /// + /// Defines a processor to fill pixels withing a given + /// with the given and blending defined by the given . + /// public class FillRegionProcessor : IImageProcessor { /// @@ -29,7 +27,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing } /// - /// Gets the brush. + /// Gets the used for filling the destination image. /// public IBrush Brush { get; } @@ -39,11 +37,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing public Region Region { get; } /// - /// Gets the options. + /// Gets the defining how to blend the brush pixels over the image pixels. /// - /// - /// The options. - /// public GraphicsOptions Options { get; } /// @@ -53,184 +48,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing return new FillRegionProcessor(this); } } - - /// - /// Using a brush and a shape fills shape with contents of brush the - /// - /// The type of the color. - /// - internal class FillRegionProcessor : ImageProcessor - where TPixel : struct, IPixel - { - private readonly FillRegionProcessor definition; - - public FillRegionProcessor(FillRegionProcessor definition) - { - this.definition = definition; - } - - /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - { - GraphicsOptions options = this.definition.Options; - IBrush brush = this.definition.Brush; - Region region = this.definition.Region; - Rectangle rect = region.Bounds; - - // Align start/end positions. - int minX = Math.Max(0, rect.Left); - int maxX = Math.Min(source.Width, rect.Right); - int minY = Math.Max(0, rect.Top); - int maxY = Math.Min(source.Height, rect.Bottom); - if (minX >= maxX) - { - return; // no effect inside image; - } - - if (minY >= maxY) - { - return; // no effect inside image; - } - - int maxIntersections = region.MaxIntersections; - float subpixelCount = 4; - - // we need to offset the pixel grid to account for when we outline a path. - // basically if the line is [1,2] => [3,2] then when outlining at 1 we end up with a region of [0.5,1.5],[1.5, 1.5],[3.5,2.5],[2.5,2.5] - // and this can cause missed fills when not using antialiasing.so we offset the pixel grid by 0.5 in the x & y direction thus causing the# - // region to align with the pixel grid. - float offset = 0.5f; - if (options.Antialias) - { - offset = 0f; // we are antialiasing skip offsetting as real antialiasing should take care of offset. - subpixelCount = options.AntialiasSubpixelDepth; - if (subpixelCount < 4) - { - subpixelCount = 4; - } - } - - using (BrushApplicator applicator = brush.CreateApplicator(source, rect, options)) - { - int scanlineWidth = maxX - minX; - using (IMemoryOwner bBuffer = source.MemoryAllocator.Allocate(maxIntersections)) - using (IMemoryOwner bScanline = source.MemoryAllocator.Allocate(scanlineWidth)) - { - bool scanlineDirty = true; - float subpixelFraction = 1f / subpixelCount; - float subpixelFractionPoint = subpixelFraction / subpixelCount; - - Span buffer = bBuffer.GetSpan(); - Span scanline = bScanline.GetSpan(); - - bool isSolidBrushWithoutBlending = this.IsSolidBrushWithoutBlending(out SolidBrush solidBrush); - TPixel solidBrushColor = isSolidBrushWithoutBlending ? solidBrush.Color.ToPixel() : default; - - for (int y = minY; y < maxY; y++) - { - if (scanlineDirty) - { - scanline.Clear(); - scanlineDirty = false; - } - - float yPlusOne = y + 1; - for (float subPixel = (float)y; subPixel < yPlusOne; subPixel += subpixelFraction) - { - int pointsFound = region.Scan(subPixel + offset, buffer, configuration); - if (pointsFound == 0) - { - // nothing on this line, skip - continue; - } - - QuickSort.Sort(buffer.Slice(0, pointsFound)); - - for (int point = 0; point < pointsFound; point += 2) - { - // points will be paired up - float scanStart = buffer[point] - minX; - float scanEnd = buffer[point + 1] - minX; - int startX = (int)MathF.Floor(scanStart + offset); - int endX = (int)MathF.Floor(scanEnd + offset); - - if (startX >= 0 && startX < scanline.Length) - { - for (float x = scanStart; x < startX + 1; x += subpixelFraction) - { - scanline[startX] += subpixelFractionPoint; - scanlineDirty = true; - } - } - - if (endX >= 0 && endX < scanline.Length) - { - for (float x = endX; x < scanEnd; x += subpixelFraction) - { - scanline[endX] += subpixelFractionPoint; - scanlineDirty = true; - } - } - - int nextX = startX + 1; - endX = Math.Min(endX, scanline.Length); // reduce to end to the right edge - nextX = Math.Max(nextX, 0); - for (int x = nextX; x < endX; x++) - { - scanline[x] += subpixelFraction; - scanlineDirty = true; - } - } - } - - if (scanlineDirty) - { - if (!options.Antialias) - { - bool hasOnes = false; - bool hasZeros = false; - for (int x = 0; x < scanlineWidth; x++) - { - if (scanline[x] >= 0.5) - { - scanline[x] = 1; - hasOnes = true; - } - else - { - scanline[x] = 0; - hasZeros = true; - } - } - - if (isSolidBrushWithoutBlending && hasOnes != hasZeros) - { - if (hasOnes) - { - source.GetPixelRowSpan(y).Slice(minX, scanlineWidth).Fill(solidBrushColor); - } - - continue; - } - } - - applicator.Apply(scanline, minX, y); - } - } - } - } - } - - private bool IsSolidBrushWithoutBlending(out SolidBrush solidBrush) - { - solidBrush = this.definition.Brush as SolidBrush; - - if (solidBrush == null) - { - return false; - } - - return this.definition.Options.IsOpaqueColorWithoutBlending(solidBrush.Color); - } - } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor{TPixel}.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor{TPixel}.cs new file mode 100644 index 0000000000..d5778c865f --- /dev/null +++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor{TPixel}.cs @@ -0,0 +1,195 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Primitives; +using SixLabors.ImageSharp.Utils; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Drawing +{ + /// + /// Using a brush and a shape fills shape with contents of brush the + /// + /// The type of the color. + /// + internal class FillRegionProcessor : ImageProcessor + where TPixel : struct, IPixel + { + private readonly FillRegionProcessor definition; + + public FillRegionProcessor(FillRegionProcessor definition) + { + this.definition = definition; + } + + /// + protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + GraphicsOptions options = this.definition.Options; + IBrush brush = this.definition.Brush; + Region region = this.definition.Region; + Rectangle rect = region.Bounds; + + // Align start/end positions. + int minX = Math.Max(0, rect.Left); + int maxX = Math.Min(source.Width, rect.Right); + int minY = Math.Max(0, rect.Top); + int maxY = Math.Min(source.Height, rect.Bottom); + if (minX >= maxX) + { + return; // no effect inside image; + } + + if (minY >= maxY) + { + return; // no effect inside image; + } + + int maxIntersections = region.MaxIntersections; + float subpixelCount = 4; + + // we need to offset the pixel grid to account for when we outline a path. + // basically if the line is [1,2] => [3,2] then when outlining at 1 we end up with a region of [0.5,1.5],[1.5, 1.5],[3.5,2.5],[2.5,2.5] + // and this can cause missed fills when not using antialiasing.so we offset the pixel grid by 0.5 in the x & y direction thus causing the# + // region to align with the pixel grid. + float offset = 0.5f; + if (options.Antialias) + { + offset = 0f; // we are antialiasing skip offsetting as real antialiasing should take care of offset. + subpixelCount = options.AntialiasSubpixelDepth; + if (subpixelCount < 4) + { + subpixelCount = 4; + } + } + + using (BrushApplicator applicator = brush.CreateApplicator(source, rect, options)) + { + int scanlineWidth = maxX - minX; + using (IMemoryOwner bBuffer = source.MemoryAllocator.Allocate(maxIntersections)) + using (IMemoryOwner bScanline = source.MemoryAllocator.Allocate(scanlineWidth)) + { + bool scanlineDirty = true; + float subpixelFraction = 1f / subpixelCount; + float subpixelFractionPoint = subpixelFraction / subpixelCount; + + Span buffer = bBuffer.GetSpan(); + Span scanline = bScanline.GetSpan(); + + bool isSolidBrushWithoutBlending = this.IsSolidBrushWithoutBlending(out SolidBrush solidBrush); + TPixel solidBrushColor = isSolidBrushWithoutBlending ? solidBrush.Color.ToPixel() : default; + + for (int y = minY; y < maxY; y++) + { + if (scanlineDirty) + { + scanline.Clear(); + scanlineDirty = false; + } + + float yPlusOne = y + 1; + for (float subPixel = (float)y; subPixel < yPlusOne; subPixel += subpixelFraction) + { + int pointsFound = region.Scan(subPixel + offset, buffer, configuration); + if (pointsFound == 0) + { + // nothing on this line, skip + continue; + } + + QuickSort.Sort(buffer.Slice(0, pointsFound)); + + for (int point = 0; point < pointsFound; point += 2) + { + // points will be paired up + float scanStart = buffer[point] - minX; + float scanEnd = buffer[point + 1] - minX; + int startX = (int)MathF.Floor(scanStart + offset); + int endX = (int)MathF.Floor(scanEnd + offset); + + if (startX >= 0 && startX < scanline.Length) + { + for (float x = scanStart; x < startX + 1; x += subpixelFraction) + { + scanline[startX] += subpixelFractionPoint; + scanlineDirty = true; + } + } + + if (endX >= 0 && endX < scanline.Length) + { + for (float x = endX; x < scanEnd; x += subpixelFraction) + { + scanline[endX] += subpixelFractionPoint; + scanlineDirty = true; + } + } + + int nextX = startX + 1; + endX = Math.Min(endX, scanline.Length); // reduce to end to the right edge + nextX = Math.Max(nextX, 0); + for (int x = nextX; x < endX; x++) + { + scanline[x] += subpixelFraction; + scanlineDirty = true; + } + } + } + + if (scanlineDirty) + { + if (!options.Antialias) + { + bool hasOnes = false; + bool hasZeros = false; + for (int x = 0; x < scanlineWidth; x++) + { + if (scanline[x] >= 0.5) + { + scanline[x] = 1; + hasOnes = true; + } + else + { + scanline[x] = 0; + hasZeros = true; + } + } + + if (isSolidBrushWithoutBlending && hasOnes != hasZeros) + { + if (hasOnes) + { + source.GetPixelRowSpan(y).Slice(minX, scanlineWidth).Fill(solidBrushColor); + } + + continue; + } + } + + applicator.Apply(scanline, minX, y); + } + } + } + } + } + + private bool IsSolidBrushWithoutBlending(out SolidBrush solidBrush) + { + solidBrush = this.definition.Brush as SolidBrush; + + if (solidBrush == null) + { + return false; + } + + return this.definition.Options.IsOpaqueColorWithoutBlending(solidBrush.Color); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs b/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs index d1e28afeb8..40621ce997 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs @@ -2,19 +2,16 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Buffers; -using System.Collections.Generic; + using SixLabors.Fonts; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Utils; -using SixLabors.Memory; using SixLabors.Primitives; -using SixLabors.Shapes; namespace SixLabors.ImageSharp.Processing.Processors.Text { + /// + /// Defines a processor to draw text on an . + /// public class DrawTextProcessor : IImageProcessor { /// @@ -45,17 +42,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text } /// - /// Gets the brush. + /// Gets the brush used to fill the glyphs. /// public IBrush Brush { get; } /// - /// Gets the options + /// Gets the defining blending modes and text-specific drawing settings. /// public TextGraphicsOptions Options { get; } /// - /// Gets the text + /// Gets the text to draw. /// public string Text { get; } @@ -81,432 +78,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text return new DrawTextProcessor(this); } } - - /// - /// Using the brush as a source of pixels colors blends the brush color with source. - /// - /// The pixel format. - internal class DrawTextProcessor : ImageProcessor - where TPixel : struct, IPixel - { - private CachingGlyphRenderer textRenderer; - - private readonly DrawTextProcessor definition; - - public DrawTextProcessor(DrawTextProcessor definition) - { - this.definition = definition; - } - - private TextGraphicsOptions Options => this.definition.Options; - - private Font Font => this.definition.Font; - - private PointF Location => this.definition.Location; - - private string Text => this.definition.Text; - - private IPen Pen => this.definition.Pen; - - private IBrush Brush => this.definition.Brush; - - protected override void BeforeImageApply(Image source, Rectangle sourceRectangle) - { - base.BeforeImageApply(source, sourceRectangle); - - // do everything at the image level as we are delegating the processing down to other processors - var style = new RendererOptions(this.Font, this.Options.DpiX, this.Options.DpiY, this.Location) - { - ApplyKerning = this.Options.ApplyKerning, - TabWidth = this.Options.TabWidth, - WrappingWidth = this.Options.WrapTextWidth, - HorizontalAlignment = this.Options.HorizontalAlignment, - VerticalAlignment = this.Options.VerticalAlignment - }; - - this.textRenderer = new CachingGlyphRenderer(source.GetMemoryAllocator(), this.Text.Length, this.Pen, this.Brush != null); - this.textRenderer.Options = (GraphicsOptions)this.Options; - var renderer = new TextRenderer(this.textRenderer); - renderer.RenderText(this.Text, style); - } - - protected override void AfterImageApply(Image source, Rectangle sourceRectangle) - { - base.AfterImageApply(source, sourceRectangle); - this.textRenderer?.Dispose(); - this.textRenderer = null; - } - - /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - { - // this is a no-op as we have processes all as an image, we should be able to pass out of before email apply a skip frames outcome - Draw(this.textRenderer.FillOperations, this.Brush); - Draw(this.textRenderer.OutlineOperations, this.Pen?.StrokeFill); - - void Draw(List operations, IBrush brush) - { - if (operations?.Count > 0) - { - using (BrushApplicator app = brush.CreateApplicator(source, sourceRectangle, this.textRenderer.Options)) - { - foreach (DrawingOperation operation in operations) - { - Buffer2D buffer = operation.Map; - int startY = operation.Location.Y; - int startX = operation.Location.X; - int offSetSpan = 0; - if (startX < 0) - { - offSetSpan = -startX; - startX = 0; - } - - int fistRow = 0; - if (startY < 0) - { - fistRow = -startY; - } - - int maxHeight = source.Height - startY; - int end = Math.Min(operation.Map.Height, maxHeight); - - for (int row = fistRow; row < end; row++) - { - int y = startY + row; - Span span = buffer.GetRowSpan(row).Slice(offSetSpan); - app.Apply(span, startX, y); - } - } - } - } - } - } - - private struct DrawingOperation - { - public Buffer2D Map { get; set; } - - public Point Location { get; set; } - } - - private class CachingGlyphRenderer : IGlyphRenderer, IDisposable - { - // just enough accuracy to allow for 1/8 pixel differences which - // later are accumulated while rendering, but do not grow into full pixel offsets - // The value 8 is benchmarked to: - // - Provide a good accuracy (smaller than 0.2% image difference compared to the non-caching variant) - // - Cache hit ratio above 60% - private const float AccuracyMultiple = 8; - - private readonly PathBuilder builder; - - private Point currentRenderPosition = default; - private (GlyphRendererParameters glyph, PointF subPixelOffset) currentGlyphRenderParams = default; - private readonly int offset = 0; - private PointF currentPoint = default(PointF); - - private readonly Dictionary<(GlyphRendererParameters glyph, PointF subPixelOffset), GlyphRenderData> - glyphData = new Dictionary<(GlyphRendererParameters glyph, PointF subPixelOffset), GlyphRenderData>(); - - private readonly bool renderOutline = false; - private readonly bool renderFill = false; - private bool rasterizationRequired = false; - - public CachingGlyphRenderer(MemoryAllocator memoryAllocator, int size, IPen pen, bool renderFill) - { - this.MemoryAllocator = memoryAllocator; - this.Pen = pen; - this.renderFill = renderFill; - this.renderOutline = pen != null; - this.offset = 2; - if (this.renderFill) - { - this.FillOperations = new List(size); - } - - if (this.renderOutline) - { - this.offset = (int)MathF.Ceiling((pen.StrokeWidth * 2) + 2); - this.OutlineOperations = new List(size); - } - - this.builder = new PathBuilder(); - } - - public List FillOperations { get; } - - public List OutlineOperations { get; } - - public MemoryAllocator MemoryAllocator { get; internal set; } - - public IPen Pen { get; internal set; } - - public GraphicsOptions Options { get; internal set; } - - public void BeginFigure() - { - this.builder.StartFigure(); - } - - public bool BeginGlyph(RectangleF bounds, GlyphRendererParameters parameters) - { - this.currentRenderPosition = Point.Truncate(bounds.Location); - PointF subPixelOffset = bounds.Location - this.currentRenderPosition; - - subPixelOffset.X = MathF.Round(subPixelOffset.X * AccuracyMultiple) / AccuracyMultiple; - subPixelOffset.Y = MathF.Round(subPixelOffset.Y * AccuracyMultiple) / AccuracyMultiple; - - // we have offset our rendering origion a little bit down to prevent edge cropping, move the draw origin up to compensate - this.currentRenderPosition = new Point(this.currentRenderPosition.X - this.offset, this.currentRenderPosition.Y - this.offset); - this.currentGlyphRenderParams = (parameters, subPixelOffset); - - if (this.glyphData.ContainsKey(this.currentGlyphRenderParams)) - { - // we have already drawn the glyph vectors skip trying again - this.rasterizationRequired = false; - return false; - } - - // we check to see if we have a render cache and if we do then we render else - this.builder.Clear(); - - // ensure all glyphs render around [zero, zero] so offset negative root positions so when we draw the glyph we can offet it back - this.builder.SetOrigin(new PointF(-(int)bounds.X + this.offset, -(int)bounds.Y + this.offset)); - - this.rasterizationRequired = true; - return true; - } - - public void BeginText(RectangleF bounds) - { - // not concerned about this one - this.OutlineOperations?.Clear(); - this.FillOperations?.Clear(); - } - - public void CubicBezierTo(PointF secondControlPoint, PointF thirdControlPoint, PointF point) - { - this.builder.AddBezier(this.currentPoint, secondControlPoint, thirdControlPoint, point); - this.currentPoint = point; - } - - public void Dispose() - { - foreach (KeyValuePair<(GlyphRendererParameters glyph, PointF subPixelOffset), GlyphRenderData> kv in this.glyphData) - { - kv.Value.Dispose(); - } - - this.glyphData.Clear(); - } - - public void EndFigure() - { - this.builder.CloseFigure(); - } - - public void EndGlyph() - { - GlyphRenderData renderData = default; - - // has the glyoh been rendedered already???? - if (this.rasterizationRequired) - { - IPath path = this.builder.Build(); - - if (this.renderFill) - { - renderData.FillMap = this.Render(path); - } - - if (this.renderOutline) - { - if (this.Pen.StrokePattern.Length == 0) - { - path = path.GenerateOutline(this.Pen.StrokeWidth); - } - else - { - path = path.GenerateOutline(this.Pen.StrokeWidth, this.Pen.StrokePattern); - } - - renderData.OutlineMap = this.Render(path); - } - - this.glyphData[this.currentGlyphRenderParams] = renderData; - } - else - { - renderData = this.glyphData[this.currentGlyphRenderParams]; - } - - if (this.renderFill) - { - this.FillOperations.Add(new DrawingOperation - { - Location = this.currentRenderPosition, - Map = renderData.FillMap - }); - } - - if (this.renderOutline) - { - this.OutlineOperations.Add(new DrawingOperation - { - Location = this.currentRenderPosition, - Map = renderData.OutlineMap - }); - } - } - - private Buffer2D Render(IPath path) - { - Size size = Rectangle.Ceiling(path.Bounds).Size; - size = new Size(size.Width + (this.offset * 2), size.Height + (this.offset * 2)); - - float subpixelCount = 4; - float offset = 0.5f; - if (this.Options.Antialias) - { - offset = 0f; // we are antialising skip offsetting as real antalising should take care of offset. - subpixelCount = this.Options.AntialiasSubpixelDepth; - if (subpixelCount < 4) - { - subpixelCount = 4; - } - } - - // take the path inside the path builder, scan thing and generate a Buffer2d representing the glyph and cache it. - Buffer2D fullBuffer = this.MemoryAllocator.Allocate2D(size.Width + 1, size.Height + 1, AllocationOptions.Clean); - - using (IMemoryOwner bufferBacking = this.MemoryAllocator.Allocate(path.MaxIntersections)) - using (IMemoryOwner rowIntersectionBuffer = this.MemoryAllocator.Allocate(size.Width)) - { - float subpixelFraction = 1f / subpixelCount; - float subpixelFractionPoint = subpixelFraction / subpixelCount; - - for (int y = 0; y <= size.Height; y++) - { - Span scanline = fullBuffer.GetRowSpan(y); - bool scanlineDirty = false; - float yPlusOne = y + 1; - - for (float subPixel = (float)y; subPixel < yPlusOne; subPixel += subpixelFraction) - { - var start = new PointF(path.Bounds.Left - 1, subPixel); - var end = new PointF(path.Bounds.Right + 1, subPixel); - Span intersectionSpan = rowIntersectionBuffer.GetSpan(); - Span buffer = bufferBacking.GetSpan(); - int pointsFound = path.FindIntersections(start, end, intersectionSpan); - - if (pointsFound == 0) - { - // nothing on this line skip - continue; - } - - for (int i = 0; i < pointsFound && i < intersectionSpan.Length; i++) - { - buffer[i] = intersectionSpan[i].X; - } - - QuickSort.Sort(buffer.Slice(0, pointsFound)); - - for (int point = 0; point < pointsFound; point += 2) - { - // points will be paired up - float scanStart = buffer[point]; - float scanEnd = buffer[point + 1]; - int startX = (int)MathF.Floor(scanStart + offset); - int endX = (int)MathF.Floor(scanEnd + offset); - - if (startX >= 0 && startX < scanline.Length) - { - for (float x = scanStart; x < startX + 1; x += subpixelFraction) - { - scanline[startX] += subpixelFractionPoint; - scanlineDirty = true; - } - } - - if (endX >= 0 && endX < scanline.Length) - { - for (float x = endX; x < scanEnd; x += subpixelFraction) - { - scanline[endX] += subpixelFractionPoint; - scanlineDirty = true; - } - } - - int nextX = startX + 1; - endX = Math.Min(endX, scanline.Length); // reduce to end to the right edge - nextX = Math.Max(nextX, 0); - for (int x = nextX; x < endX; x++) - { - scanline[x] += subpixelFraction; - scanlineDirty = true; - } - } - } - - if (scanlineDirty) - { - if (!this.Options.Antialias) - { - for (int x = 0; x < size.Width; x++) - { - if (scanline[x] >= 0.5) - { - scanline[x] = 1; - } - else - { - scanline[x] = 0; - } - } - } - } - } - } - - return fullBuffer; - } - - public void EndText() - { - } - - public void LineTo(PointF point) - { - this.builder.AddLine(this.currentPoint, point); - this.currentPoint = point; - } - - public void MoveTo(PointF point) - { - this.builder.StartFigure(); - this.currentPoint = point; - } - - public void QuadraticBezierTo(PointF secondControlPoint, PointF point) - { - this.builder.AddBezier(this.currentPoint, secondControlPoint, point); - this.currentPoint = point; - } - - private struct GlyphRenderData : IDisposable - { - public Buffer2D FillMap; - - public Buffer2D OutlineMap; - - public void Dispose() - { - this.FillMap?.Dispose(); - this.OutlineMap?.Dispose(); - } - } - } - } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor{TPixel}.cs b/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor{TPixel}.cs new file mode 100644 index 0000000000..9fb52d6bc3 --- /dev/null +++ b/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor{TPixel}.cs @@ -0,0 +1,446 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; +using System.Collections.Generic; + +using SixLabors.Fonts; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Utils; +using SixLabors.Memory; +using SixLabors.Primitives; +using SixLabors.Shapes; + +namespace SixLabors.ImageSharp.Processing.Processors.Text +{ + /// + /// Using the brush as a source of pixels colors blends the brush color with source. + /// + /// The pixel format. + internal class DrawTextProcessor : ImageProcessor + where TPixel : struct, IPixel + { + private CachingGlyphRenderer textRenderer; + + private readonly DrawTextProcessor definition; + + public DrawTextProcessor(DrawTextProcessor definition) + { + this.definition = definition; + } + + private TextGraphicsOptions Options => this.definition.Options; + + private Font Font => this.definition.Font; + + private PointF Location => this.definition.Location; + + private string Text => this.definition.Text; + + private IPen Pen => this.definition.Pen; + + private IBrush Brush => this.definition.Brush; + + protected override void BeforeImageApply(Image source, Rectangle sourceRectangle) + { + base.BeforeImageApply(source, sourceRectangle); + + // do everything at the image level as we are delegating the processing down to other processors + var style = new RendererOptions(this.Font, this.Options.DpiX, this.Options.DpiY, this.Location) + { + ApplyKerning = this.Options.ApplyKerning, + TabWidth = this.Options.TabWidth, + WrappingWidth = this.Options.WrapTextWidth, + HorizontalAlignment = this.Options.HorizontalAlignment, + VerticalAlignment = this.Options.VerticalAlignment + }; + + this.textRenderer = new CachingGlyphRenderer(source.GetMemoryAllocator(), this.Text.Length, this.Pen, this.Brush != null); + this.textRenderer.Options = (GraphicsOptions)this.Options; + var renderer = new TextRenderer(this.textRenderer); + renderer.RenderText(this.Text, style); + } + + protected override void AfterImageApply(Image source, Rectangle sourceRectangle) + { + base.AfterImageApply(source, sourceRectangle); + this.textRenderer?.Dispose(); + this.textRenderer = null; + } + + /// + protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + // this is a no-op as we have processes all as an image, we should be able to pass out of before email apply a skip frames outcome + Draw(this.textRenderer.FillOperations, this.Brush); + Draw(this.textRenderer.OutlineOperations, this.Pen?.StrokeFill); + + void Draw(List operations, IBrush brush) + { + if (operations?.Count > 0) + { + using (BrushApplicator app = brush.CreateApplicator(source, sourceRectangle, this.textRenderer.Options)) + { + foreach (DrawingOperation operation in operations) + { + Buffer2D buffer = operation.Map; + int startY = operation.Location.Y; + int startX = operation.Location.X; + int offSetSpan = 0; + if (startX < 0) + { + offSetSpan = -startX; + startX = 0; + } + + int fistRow = 0; + if (startY < 0) + { + fistRow = -startY; + } + + int maxHeight = source.Height - startY; + int end = Math.Min(operation.Map.Height, maxHeight); + + for (int row = fistRow; row < end; row++) + { + int y = startY + row; + Span span = buffer.GetRowSpan(row).Slice(offSetSpan); + app.Apply(span, startX, y); + } + } + } + } + } + } + + private struct DrawingOperation + { + public Buffer2D Map { get; set; } + + public Point Location { get; set; } + } + + private class CachingGlyphRenderer : IGlyphRenderer, IDisposable + { + // just enough accuracy to allow for 1/8 pixel differences which + // later are accumulated while rendering, but do not grow into full pixel offsets + // The value 8 is benchmarked to: + // - Provide a good accuracy (smaller than 0.2% image difference compared to the non-caching variant) + // - Cache hit ratio above 60% + private const float AccuracyMultiple = 8; + + private readonly PathBuilder builder; + + private Point currentRenderPosition = default; + private (GlyphRendererParameters glyph, PointF subPixelOffset) currentGlyphRenderParams = default; + private readonly int offset = 0; + private PointF currentPoint = default(PointF); + + private readonly Dictionary<(GlyphRendererParameters glyph, PointF subPixelOffset), GlyphRenderData> + glyphData = new Dictionary<(GlyphRendererParameters glyph, PointF subPixelOffset), GlyphRenderData>(); + + private readonly bool renderOutline = false; + private readonly bool renderFill = false; + private bool rasterizationRequired = false; + + public CachingGlyphRenderer(MemoryAllocator memoryAllocator, int size, IPen pen, bool renderFill) + { + this.MemoryAllocator = memoryAllocator; + this.Pen = pen; + this.renderFill = renderFill; + this.renderOutline = pen != null; + this.offset = 2; + if (this.renderFill) + { + this.FillOperations = new List(size); + } + + if (this.renderOutline) + { + this.offset = (int)MathF.Ceiling((pen.StrokeWidth * 2) + 2); + this.OutlineOperations = new List(size); + } + + this.builder = new PathBuilder(); + } + + public List FillOperations { get; } + + public List OutlineOperations { get; } + + public MemoryAllocator MemoryAllocator { get; internal set; } + + public IPen Pen { get; internal set; } + + public GraphicsOptions Options { get; internal set; } + + public void BeginFigure() + { + this.builder.StartFigure(); + } + + public bool BeginGlyph(RectangleF bounds, GlyphRendererParameters parameters) + { + this.currentRenderPosition = Point.Truncate(bounds.Location); + PointF subPixelOffset = bounds.Location - this.currentRenderPosition; + + subPixelOffset.X = MathF.Round(subPixelOffset.X * AccuracyMultiple) / AccuracyMultiple; + subPixelOffset.Y = MathF.Round(subPixelOffset.Y * AccuracyMultiple) / AccuracyMultiple; + + // we have offset our rendering origion a little bit down to prevent edge cropping, move the draw origin up to compensate + this.currentRenderPosition = new Point(this.currentRenderPosition.X - this.offset, this.currentRenderPosition.Y - this.offset); + this.currentGlyphRenderParams = (parameters, subPixelOffset); + + if (this.glyphData.ContainsKey(this.currentGlyphRenderParams)) + { + // we have already drawn the glyph vectors skip trying again + this.rasterizationRequired = false; + return false; + } + + // we check to see if we have a render cache and if we do then we render else + this.builder.Clear(); + + // ensure all glyphs render around [zero, zero] so offset negative root positions so when we draw the glyph we can offet it back + this.builder.SetOrigin(new PointF(-(int)bounds.X + this.offset, -(int)bounds.Y + this.offset)); + + this.rasterizationRequired = true; + return true; + } + + public void BeginText(RectangleF bounds) + { + // not concerned about this one + this.OutlineOperations?.Clear(); + this.FillOperations?.Clear(); + } + + public void CubicBezierTo(PointF secondControlPoint, PointF thirdControlPoint, PointF point) + { + this.builder.AddBezier(this.currentPoint, secondControlPoint, thirdControlPoint, point); + this.currentPoint = point; + } + + public void Dispose() + { + foreach (KeyValuePair<(GlyphRendererParameters glyph, PointF subPixelOffset), GlyphRenderData> kv in this.glyphData) + { + kv.Value.Dispose(); + } + + this.glyphData.Clear(); + } + + public void EndFigure() + { + this.builder.CloseFigure(); + } + + public void EndGlyph() + { + GlyphRenderData renderData = default; + + // has the glyoh been rendedered already???? + if (this.rasterizationRequired) + { + IPath path = this.builder.Build(); + + if (this.renderFill) + { + renderData.FillMap = this.Render(path); + } + + if (this.renderOutline) + { + if (this.Pen.StrokePattern.Length == 0) + { + path = path.GenerateOutline(this.Pen.StrokeWidth); + } + else + { + path = path.GenerateOutline(this.Pen.StrokeWidth, this.Pen.StrokePattern); + } + + renderData.OutlineMap = this.Render(path); + } + + this.glyphData[this.currentGlyphRenderParams] = renderData; + } + else + { + renderData = this.glyphData[this.currentGlyphRenderParams]; + } + + if (this.renderFill) + { + this.FillOperations.Add(new DrawingOperation + { + Location = this.currentRenderPosition, + Map = renderData.FillMap + }); + } + + if (this.renderOutline) + { + this.OutlineOperations.Add(new DrawingOperation + { + Location = this.currentRenderPosition, + Map = renderData.OutlineMap + }); + } + } + + private Buffer2D Render(IPath path) + { + Size size = Rectangle.Ceiling(path.Bounds).Size; + size = new Size(size.Width + (this.offset * 2), size.Height + (this.offset * 2)); + + float subpixelCount = 4; + float offset = 0.5f; + if (this.Options.Antialias) + { + offset = 0f; // we are antialising skip offsetting as real antalising should take care of offset. + subpixelCount = this.Options.AntialiasSubpixelDepth; + if (subpixelCount < 4) + { + subpixelCount = 4; + } + } + + // take the path inside the path builder, scan thing and generate a Buffer2d representing the glyph and cache it. + Buffer2D fullBuffer = this.MemoryAllocator.Allocate2D(size.Width + 1, size.Height + 1, AllocationOptions.Clean); + + using (IMemoryOwner bufferBacking = this.MemoryAllocator.Allocate(path.MaxIntersections)) + using (IMemoryOwner rowIntersectionBuffer = this.MemoryAllocator.Allocate(size.Width)) + { + float subpixelFraction = 1f / subpixelCount; + float subpixelFractionPoint = subpixelFraction / subpixelCount; + + for (int y = 0; y <= size.Height; y++) + { + Span scanline = fullBuffer.GetRowSpan(y); + bool scanlineDirty = false; + float yPlusOne = y + 1; + + for (float subPixel = (float)y; subPixel < yPlusOne; subPixel += subpixelFraction) + { + var start = new PointF(path.Bounds.Left - 1, subPixel); + var end = new PointF(path.Bounds.Right + 1, subPixel); + Span intersectionSpan = rowIntersectionBuffer.GetSpan(); + Span buffer = bufferBacking.GetSpan(); + int pointsFound = path.FindIntersections(start, end, intersectionSpan); + + if (pointsFound == 0) + { + // nothing on this line skip + continue; + } + + for (int i = 0; i < pointsFound && i < intersectionSpan.Length; i++) + { + buffer[i] = intersectionSpan[i].X; + } + + QuickSort.Sort(buffer.Slice(0, pointsFound)); + + for (int point = 0; point < pointsFound; point += 2) + { + // points will be paired up + float scanStart = buffer[point]; + float scanEnd = buffer[point + 1]; + int startX = (int)MathF.Floor(scanStart + offset); + int endX = (int)MathF.Floor(scanEnd + offset); + + if (startX >= 0 && startX < scanline.Length) + { + for (float x = scanStart; x < startX + 1; x += subpixelFraction) + { + scanline[startX] += subpixelFractionPoint; + scanlineDirty = true; + } + } + + if (endX >= 0 && endX < scanline.Length) + { + for (float x = endX; x < scanEnd; x += subpixelFraction) + { + scanline[endX] += subpixelFractionPoint; + scanlineDirty = true; + } + } + + int nextX = startX + 1; + endX = Math.Min(endX, scanline.Length); // reduce to end to the right edge + nextX = Math.Max(nextX, 0); + for (int x = nextX; x < endX; x++) + { + scanline[x] += subpixelFraction; + scanlineDirty = true; + } + } + } + + if (scanlineDirty) + { + if (!this.Options.Antialias) + { + for (int x = 0; x < size.Width; x++) + { + if (scanline[x] >= 0.5) + { + scanline[x] = 1; + } + else + { + scanline[x] = 0; + } + } + } + } + } + } + + return fullBuffer; + } + + public void EndText() + { + } + + public void LineTo(PointF point) + { + this.builder.AddLine(this.currentPoint, point); + this.currentPoint = point; + } + + public void MoveTo(PointF point) + { + this.builder.StartFigure(); + this.currentPoint = point; + } + + public void QuadraticBezierTo(PointF secondControlPoint, PointF point) + { + this.builder.AddBezier(this.currentPoint, secondControlPoint, point); + this.currentPoint = point; + } + + private struct GlyphRenderData : IDisposable + { + public Buffer2D FillMap; + + public Buffer2D OutlineMap; + + public void Dispose() + { + this.FillMap?.Dispose(); + this.OutlineMap?.Dispose(); + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/RadialGradientBrush.cs b/src/ImageSharp.Drawing/Processing/RadialGradientBrush.cs index 84a97e7d1f..aac0f2e7bd 100644 --- a/src/ImageSharp.Drawing/Processing/RadialGradientBrush.cs +++ b/src/ImageSharp.Drawing/Processing/RadialGradientBrush.cs @@ -12,7 +12,6 @@ namespace SixLabors.ImageSharp.Processing /// A Circular Gradient Brush, defined by center point and radius. /// public sealed class RadialGradientBrush : GradientBrushBase - { private readonly PointF center; diff --git a/src/ImageSharp.Drawing/Processing/SolidBrush.cs b/src/ImageSharp.Drawing/Processing/SolidBrush.cs index 75ca5aa7e8..c62566f6b7 100644 --- a/src/ImageSharp.Drawing/Processing/SolidBrush.cs +++ b/src/ImageSharp.Drawing/Processing/SolidBrush.cs @@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Processing private readonly Color color; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The color. public SolidBrush(Color color) @@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.Processing where TPixel : struct, IPixel { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The source image. /// The color. diff --git a/src/ImageSharp.Drawing/Processing/TextGraphicsOptions.cs b/src/ImageSharp.Drawing/Processing/TextGraphicsOptions.cs index 3c682a761b..7f7332a57c 100644 --- a/src/ImageSharp.Drawing/Processing/TextGraphicsOptions.cs +++ b/src/ImageSharp.Drawing/Processing/TextGraphicsOptions.cs @@ -1,169 +1,163 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.Fonts; -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp.Processing -{ - /// - /// Options for influencing the drawing functions. - /// - public struct TextGraphicsOptions - { - private const int DefaultTextDpi = 72; - - /// - /// Represents the default . - /// - public static readonly TextGraphicsOptions Default = new TextGraphicsOptions(true); - - private float? blendPercentage; - - private int? antialiasSubpixelDepth; - - private bool? antialias; - - private bool? applyKerning; - - private float? tabWidth; - - private float? dpiX; - - private float? dpiY; - - private PixelColorBlendingMode colorBlendingMode; - - private PixelAlphaCompositionMode alphaCompositionMode; - - private float wrapTextWidth; - - private HorizontalAlignment? horizontalAlignment; - - private VerticalAlignment? verticalAlignment; - - /// - /// Initializes a new instance of the struct. - /// - /// If set to true [enable antialiasing]. - public TextGraphicsOptions(bool enableAntialiasing) - { - this.applyKerning = true; - this.tabWidth = 4; - this.wrapTextWidth = 0; - this.horizontalAlignment = HorizontalAlignment.Left; - this.verticalAlignment = VerticalAlignment.Top; - - this.antialiasSubpixelDepth = 16; - this.colorBlendingMode = PixelColorBlendingMode.Normal; - this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver; - this.blendPercentage = 1; - this.antialias = enableAntialiasing; - this.dpiX = DefaultTextDpi; - this.dpiY = DefaultTextDpi; - } - - /// - /// Gets or sets a value indicating whether antialiasing should be applied. - /// - public bool Antialias { get => this.antialias ?? true; set => this.antialias = value; } - - /// - /// Gets or sets a value indicating the number of subpixels to use while rendering with antialiasing enabled. - /// - public int AntialiasSubpixelDepth { get => this.antialiasSubpixelDepth ?? 16; set => this.antialiasSubpixelDepth = value; } - - /// - /// Gets or sets a value indicating the blending percentage to apply to the drawing operation - /// - public float BlendPercentage { get => (this.blendPercentage ?? 1).Clamp(0, 1); set => this.blendPercentage = value; } - - // In the future we could expose a PixelBlender directly on here - // or some forms of PixelBlender factory for each pixel type. Will need - // some API thought post V1. - - /// - /// Gets or sets a value indicating the color blending percentage to apply to the drawing operation - /// - public PixelColorBlendingMode ColorBlendingMode { get => this.colorBlendingMode; set => this.colorBlendingMode = value; } - - /// - /// Gets or sets a value indicating the color blending percentage to apply to the drawing operation - /// - public PixelAlphaCompositionMode AlphaCompositionMode { get => this.alphaCompositionMode; set => this.alphaCompositionMode = value; } - - /// - /// Gets or sets a value indicating whether the text should be drawing with kerning enabled. - /// - public bool ApplyKerning { get => this.applyKerning ?? true; set => this.applyKerning = value; } - - /// - /// Gets or sets a value indicating the number of space widths a tab should lock to. - /// - public float TabWidth { get => this.tabWidth ?? 4; set => this.tabWidth = value; } - - /// - /// Gets or sets a value indicating if greater than zero determine the width at which text should wrap. - /// - public float WrapTextWidth { get => this.wrapTextWidth; set => this.wrapTextWidth = value; } - - /// - /// Gets or sets a value indicating the DPI to render text along the X axis. - /// - public float DpiX { get => this.dpiX ?? DefaultTextDpi; set => this.dpiX = value; } - - /// - /// Gets or sets a value indicating the DPI to render text along the Y axis. - /// - public float DpiY { get => this.dpiY ?? DefaultTextDpi; set => this.dpiY = value; } - - /// - /// Gets or sets a value indicating how to align the text relative to the rendering space. - /// If is greater than zero it will align relative to the space - /// defined by the location and width, if equals zero, and thus - /// wrapping disabled, then the alignment is relative to the drawing location. - /// - public HorizontalAlignment HorizontalAlignment { get => this.horizontalAlignment ?? HorizontalAlignment.Left; set => this.horizontalAlignment = value; } - - /// - /// Gets or sets a value indicating how to align the text relative to the rendering space. - /// - public VerticalAlignment VerticalAlignment { get => this.verticalAlignment ?? VerticalAlignment.Top; set => this.verticalAlignment = value; } - - /// - /// Performs an implicit conversion from to . - /// - /// The options. - /// - /// The result of the conversion. - /// - public static implicit operator TextGraphicsOptions(GraphicsOptions options) - { - return new TextGraphicsOptions(options.Antialias) - { - AntialiasSubpixelDepth = options.AntialiasSubpixelDepth, - blendPercentage = options.BlendPercentage, - colorBlendingMode = options.ColorBlendingMode, - alphaCompositionMode = options.AlphaCompositionMode - }; - } - - /// - /// Performs an explicit conversion from to . - /// - /// The options. - /// - /// The result of the conversion. - /// - public static explicit operator GraphicsOptions(TextGraphicsOptions options) - { - return new GraphicsOptions(options.Antialias) - { - AntialiasSubpixelDepth = options.AntialiasSubpixelDepth, +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.Fonts; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing +{ + /// + /// Options for influencing the drawing functions. + /// + public struct TextGraphicsOptions + { + private const int DefaultTextDpi = 72; + + /// + /// Represents the default . + /// + public static readonly TextGraphicsOptions Default = new TextGraphicsOptions(true); + + private float? blendPercentage; + + private int? antialiasSubpixelDepth; + + private bool? antialias; + + private bool? applyKerning; + + private float? tabWidth; + + private float? dpiX; + + private float? dpiY; + + private HorizontalAlignment? horizontalAlignment; + + private VerticalAlignment? verticalAlignment; + + /// + /// Initializes a new instance of the struct. + /// + /// If set to true [enable antialiasing]. + public TextGraphicsOptions(bool enableAntialiasing) + { + this.applyKerning = true; + this.tabWidth = 4; + this.WrapTextWidth = 0; + this.horizontalAlignment = HorizontalAlignment.Left; + this.verticalAlignment = VerticalAlignment.Top; + + this.antialiasSubpixelDepth = 16; + this.ColorBlendingMode = PixelColorBlendingMode.Normal; + this.AlphaCompositionMode = PixelAlphaCompositionMode.SrcOver; + this.blendPercentage = 1; + this.antialias = enableAntialiasing; + this.dpiX = DefaultTextDpi; + this.dpiY = DefaultTextDpi; + } + + /// + /// Gets or sets a value indicating whether antialiasing should be applied. + /// + public bool Antialias { get => this.antialias ?? true; set => this.antialias = value; } + + /// + /// Gets or sets a value indicating the number of subpixels to use while rendering with antialiasing enabled. + /// + public int AntialiasSubpixelDepth { get => this.antialiasSubpixelDepth ?? 16; set => this.antialiasSubpixelDepth = value; } + + /// + /// Gets or sets a value indicating the blending percentage to apply to the drawing operation + /// + public float BlendPercentage { get => (this.blendPercentage ?? 1).Clamp(0, 1); set => this.blendPercentage = value; } + + // In the future we could expose a PixelBlender directly on here + // or some forms of PixelBlender factory for each pixel type. Will need + // some API thought post V1. + + /// + /// Gets or sets a value indicating the color blending percentage to apply to the drawing operation + /// + public PixelColorBlendingMode ColorBlendingMode { get; set; } + + /// + /// Gets or sets a value indicating the color blending percentage to apply to the drawing operation + /// + public PixelAlphaCompositionMode AlphaCompositionMode { get; set; } + + /// + /// Gets or sets a value indicating whether the text should be drawing with kerning enabled. + /// + public bool ApplyKerning { get => this.applyKerning ?? true; set => this.applyKerning = value; } + + /// + /// Gets or sets a value indicating the number of space widths a tab should lock to. + /// + public float TabWidth { get => this.tabWidth ?? 4; set => this.tabWidth = value; } + + /// + /// Gets or sets a value indicating if greater than zero determine the width at which text should wrap. + /// + public float WrapTextWidth { get; set; } + + /// + /// Gets or sets a value indicating the DPI to render text along the X axis. + /// + public float DpiX { get => this.dpiX ?? DefaultTextDpi; set => this.dpiX = value; } + + /// + /// Gets or sets a value indicating the DPI to render text along the Y axis. + /// + public float DpiY { get => this.dpiY ?? DefaultTextDpi; set => this.dpiY = value; } + + /// + /// Gets or sets a value indicating how to align the text relative to the rendering space. + /// If is greater than zero it will align relative to the space + /// defined by the location and width, if equals zero, and thus + /// wrapping disabled, then the alignment is relative to the drawing location. + /// + public HorizontalAlignment HorizontalAlignment { get => this.horizontalAlignment ?? HorizontalAlignment.Left; set => this.horizontalAlignment = value; } + + /// + /// Gets or sets a value indicating how to align the text relative to the rendering space. + /// + public VerticalAlignment VerticalAlignment { get => this.verticalAlignment ?? VerticalAlignment.Top; set => this.verticalAlignment = value; } + + /// + /// Performs an implicit conversion from to . + /// + /// The options. + /// + /// The result of the conversion. + /// + public static implicit operator TextGraphicsOptions(GraphicsOptions options) + { + return new TextGraphicsOptions(options.Antialias) + { + AntialiasSubpixelDepth = options.AntialiasSubpixelDepth, + blendPercentage = options.BlendPercentage, + ColorBlendingMode = options.ColorBlendingMode, + AlphaCompositionMode = options.AlphaCompositionMode + }; + } + + /// + /// Performs an explicit conversion from to . + /// + /// The options. + /// + /// The result of the conversion. + /// + public static explicit operator GraphicsOptions(TextGraphicsOptions options) + { + return new GraphicsOptions(options.Antialias) + { + AntialiasSubpixelDepth = options.AntialiasSubpixelDepth, ColorBlendingMode = options.ColorBlendingMode, - AlphaCompositionMode = options.AlphaCompositionMode, - BlendPercentage = options.BlendPercentage - }; - } - } + AlphaCompositionMode = options.AlphaCompositionMode, + BlendPercentage = options.BlendPercentage + }; + } + } } \ No newline at end of file diff --git a/src/ImageSharp/Image.cs b/src/ImageSharp/Image.cs index db03281064..3f3d6a62f7 100644 --- a/src/ImageSharp/Image.cs +++ b/src/ImageSharp/Image.cs @@ -87,7 +87,7 @@ namespace SixLabors.ImageSharp EncodeVisitor visitor = new EncodeVisitor(encoder, stream); this.AcceptVisitor(visitor); } - + /// /// Returns a copy of the image in the given pixel format. /// diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index 4abe2f57a2..1b213c26dc 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -41,7 +41,7 @@ - + From e96c0e83ae6e9194fd4a3f158b82a520791f4c82 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 13 May 2019 23:59:50 +0200 Subject: [PATCH 190/223] fix remaining stylecop issues --- .../Processing/DrawingHelpers.cs | 7 ++- .../Extensions/DrawPathExtensions.cs | 58 +++++++++++-------- .../Extensions/FillPathExtensions.cs | 33 +++++------ .../Extensions/FillRegionExtensions.cs | 54 ++++++++--------- .../Processing/GradientBrushBase.cs | 2 +- .../Processing/GradientRepetitionMode.cs | 6 +- src/ImageSharp.Drawing/Processing/IBrush.cs | 4 +- src/ImageSharp.Drawing/Processing/IPen.cs | 5 +- .../Processing/ImageBrush.cs | 2 +- .../Processing/LinearGradientBrush.cs | 18 ++++-- 10 files changed, 102 insertions(+), 87 deletions(-) diff --git a/src/ImageSharp.Drawing/Processing/DrawingHelpers.cs b/src/ImageSharp.Drawing/Processing/DrawingHelpers.cs index 314b65f7e7..b4ce0c5cba 100644 --- a/src/ImageSharp.Drawing/Processing/DrawingHelpers.cs +++ b/src/ImageSharp.Drawing/Processing/DrawingHelpers.cs @@ -1,5 +1,5 @@ -// // Copyright (c) Six Labors and contributors. -// // Licensed under the Apache License, Version 2.0. +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; @@ -8,6 +8,9 @@ namespace SixLabors.ImageSharp.Processing { internal static class DrawingHelpers { + /// + /// Convert a to a of the given pixel type. + /// public static DenseMatrix ToPixelMatrix(this DenseMatrix colorMatrix, Configuration configuration) where TPixel : struct, IPixel { diff --git a/src/ImageSharp.Drawing/Processing/Extensions/DrawPathExtensions.cs b/src/ImageSharp.Drawing/Processing/Extensions/DrawPathExtensions.cs index f231f1baae..efafdde5ab 100644 --- a/src/ImageSharp.Drawing/Processing/Extensions/DrawPathExtensions.cs +++ b/src/ImageSharp.Drawing/Processing/Extensions/DrawPathExtensions.cs @@ -15,80 +15,90 @@ namespace SixLabors.ImageSharp.Processing /// /// Draws the outline of the polygon with the provided pen. /// - /// The image this method extends. /// The options. /// The pen. /// The path. /// The . - public static IImageProcessingContext Draw(this IImageProcessingContext source, GraphicsOptions options, IPen pen, IPath path) - - => source.Fill(options, pen.StrokeFill, new ShapePath(path, pen)); + public static IImageProcessingContext Draw( + this IImageProcessingContext source, + GraphicsOptions options, + IPen pen, + IPath path) => + source.Fill(options, pen.StrokeFill, new ShapePath(path, pen)); /// /// Draws the outline of the polygon with the provided pen. /// - /// The image this method extends. /// The pen. /// The path. /// The . - public static IImageProcessingContext Draw(this IImageProcessingContext source, IPen pen, IPath path) - - => source.Draw(GraphicsOptions.Default, pen, path); + public static IImageProcessingContext Draw(this IImageProcessingContext source, IPen pen, IPath path) => + source.Draw(GraphicsOptions.Default, pen, path); /// /// Draws the outline of the polygon with the provided brush at the provided thickness. /// - /// The image this method extends. /// The options. /// The brush. /// The thickness. /// The shape. /// The . - public static IImageProcessingContext Draw(this IImageProcessingContext source, GraphicsOptions options, IBrush brush, float thickness, IPath path) - - => source.Draw(options, new Pen(brush, thickness), path); + public static IImageProcessingContext Draw( + this IImageProcessingContext source, + GraphicsOptions options, + IBrush brush, + float thickness, + IPath path) => + source.Draw(options, new Pen(brush, thickness), path); /// /// Draws the outline of the polygon with the provided brush at the provided thickness. /// - /// The image this method extends. /// The brush. /// The thickness. /// The path. /// The . - public static IImageProcessingContext Draw(this IImageProcessingContext source, IBrush brush, float thickness, IPath path) - - => source.Draw(new Pen(brush, thickness), path); + public static IImageProcessingContext Draw( + this IImageProcessingContext source, + IBrush brush, + float thickness, + IPath path) => + source.Draw(new Pen(brush, thickness), path); /// /// Draws the outline of the polygon with the provided brush at the provided thickness. /// - /// The image this method extends. /// The options. /// The color. /// The thickness. /// The path. /// The . - public static IImageProcessingContext Draw(this IImageProcessingContext source, GraphicsOptions options, Color color, float thickness, IPath path) - - => source.Draw(options, new SolidBrush(color), thickness, path); + public static IImageProcessingContext Draw( + this IImageProcessingContext source, + GraphicsOptions options, + Color color, + float thickness, + IPath path) => + source.Draw(options, new SolidBrush(color), thickness, path); /// /// Draws the outline of the polygon with the provided brush at the provided thickness. /// - /// The image this method extends. /// The color. /// The thickness. /// The path. /// The . - public static IImageProcessingContext Draw(this IImageProcessingContext source, Color color, float thickness, IPath path) - - => source.Draw(new SolidBrush(color), thickness, path); + public static IImageProcessingContext Draw( + this IImageProcessingContext source, + Color color, + float thickness, + IPath path) => + source.Draw(new SolidBrush(color), thickness, path); } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/Extensions/FillPathExtensions.cs b/src/ImageSharp.Drawing/Processing/Extensions/FillPathExtensions.cs index 7cf67612a2..718016a9e6 100644 --- a/src/ImageSharp.Drawing/Processing/Extensions/FillPathExtensions.cs +++ b/src/ImageSharp.Drawing/Processing/Extensions/FillPathExtensions.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; using SixLabors.Shapes; @@ -15,51 +14,51 @@ namespace SixLabors.ImageSharp.Processing /// /// Flood fills the image in the shape of the provided polygon with the specified brush. /// - /// The image this method extends. /// The graphics options. /// The brush. /// The shape. /// The . - public static IImageProcessingContext Fill(this IImageProcessingContext source, GraphicsOptions options, IBrush brush, IPath path) - - => source.Fill(options, brush, new ShapeRegion(path)); + public static IImageProcessingContext Fill( + this IImageProcessingContext source, + GraphicsOptions options, + IBrush brush, + IPath path) => + source.Fill(options, brush, new ShapeRegion(path)); /// /// Flood fills the image in the shape of the provided polygon with the specified brush. /// - /// The image this method extends. /// The brush. /// The path. /// The . - public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, IPath path) - - => source.Fill(GraphicsOptions.Default, brush, new ShapeRegion(path)); + public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, IPath path) => + source.Fill(GraphicsOptions.Default, brush, new ShapeRegion(path)); /// /// Flood fills the image in the shape of the provided polygon with the specified brush.. /// - /// The image this method extends. /// The options. /// The color. /// The path. /// The . - public static IImageProcessingContext Fill(this IImageProcessingContext source, GraphicsOptions options, Color color, IPath path) - - => source.Fill(options, new SolidBrush(color), path); + public static IImageProcessingContext Fill( + this IImageProcessingContext source, + GraphicsOptions options, + Color color, + IPath path) => + source.Fill(options, new SolidBrush(color), path); /// /// Flood fills the image in the shape of the provided polygon with the specified brush.. /// - /// The image this method extends. /// The color. /// The path. /// The . - public static IImageProcessingContext Fill(this IImageProcessingContext source, Color color, IPath path) - - => source.Fill(new SolidBrush(color), path); + public static IImageProcessingContext Fill(this IImageProcessingContext source, Color color, IPath path) => + source.Fill(new SolidBrush(color), path); } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/Extensions/FillRegionExtensions.cs b/src/ImageSharp.Drawing/Processing/Extensions/FillRegionExtensions.cs index 457ad98b0c..294e575140 100644 --- a/src/ImageSharp.Drawing/Processing/Extensions/FillRegionExtensions.cs +++ b/src/ImageSharp.Drawing/Processing/Extensions/FillRegionExtensions.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing.Processors.Drawing; @@ -15,85 +14,82 @@ namespace SixLabors.ImageSharp.Processing /// /// Flood fills the image with the specified brush. /// - /// The image this method extends. /// The details how to fill the region of interest. /// The . - public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush) - - => source.Fill(GraphicsOptions.Default, brush); + public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush) => + source.Fill(GraphicsOptions.Default, brush); /// /// Flood fills the image with the specified color. /// - /// The image this method extends. /// The color. /// The . - public static IImageProcessingContext Fill(this IImageProcessingContext source, Color color) - - => source.Fill(new SolidBrush(color)); + public static IImageProcessingContext Fill(this IImageProcessingContext source, Color color) => + source.Fill(new SolidBrush(color)); /// /// Flood fills the image with in the region with the specified brush. /// - /// The image this method extends. /// The brush. /// The region. /// The . - public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, Region region) - - => source.Fill(GraphicsOptions.Default, brush, region); + public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, Region region) => + source.Fill(GraphicsOptions.Default, brush, region); /// /// Flood fills the image with in the region with the specified color. /// - /// The image this method extends. /// The options. /// The color. /// The region. /// The . - public static IImageProcessingContext Fill(this IImageProcessingContext source, GraphicsOptions options, Color color, Region region) - - => source.Fill(options, new SolidBrush(color), region); + public static IImageProcessingContext Fill( + this IImageProcessingContext source, + GraphicsOptions options, + Color color, + Region region) => + source.Fill(options, new SolidBrush(color), region); /// /// Flood fills the image with in the region with the specified color. /// - /// The image this method extends. /// The color. /// The region. /// The . - public static IImageProcessingContext Fill(this IImageProcessingContext source, Color color, Region region) - - => source.Fill(new SolidBrush(color), region); + public static IImageProcessingContext Fill(this IImageProcessingContext source, Color color, Region region) => + source.Fill(new SolidBrush(color), region); /// /// Flood fills the image with in the region with the specified brush. /// - /// The image this method extends. /// The graphics options. /// The brush. /// The region. /// The . - public static IImageProcessingContext Fill(this IImageProcessingContext source, GraphicsOptions options, IBrush brush, Region region) - - => source.ApplyProcessor(new FillRegionProcessor(brush, region, options)); + public static IImageProcessingContext Fill( + this IImageProcessingContext source, + GraphicsOptions options, + IBrush brush, + Region region) => + source.ApplyProcessor(new FillRegionProcessor(brush, region, options)); /// /// Flood fills the image with the specified brush. /// - /// The image this method extends. /// The graphics options. /// The details how to fill the region of interest. /// The . - public static IImageProcessingContext Fill(this IImageProcessingContext source, GraphicsOptions options, IBrush brush) - - => source.ApplyProcessor(new FillProcessor(brush, options)); + public static IImageProcessingContext Fill( + this IImageProcessingContext source, + GraphicsOptions options, + IBrush brush) => + source.ApplyProcessor(new FillProcessor(brush, options)); } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/GradientBrushBase.cs b/src/ImageSharp.Drawing/Processing/GradientBrushBase.cs index a877ea96cd..6e1e0086da 100644 --- a/src/ImageSharp.Drawing/Processing/GradientBrushBase.cs +++ b/src/ImageSharp.Drawing/Processing/GradientBrushBase.cs @@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.Processing /// /// Base class for gradient brush applicators /// - protected abstract class GradientBrushApplicatorBase : BrushApplicator + internal abstract class GradientBrushApplicatorBase : BrushApplicator where TPixel : struct, IPixel { private readonly ColorStop[] colorStops; diff --git a/src/ImageSharp.Drawing/Processing/GradientRepetitionMode.cs b/src/ImageSharp.Drawing/Processing/GradientRepetitionMode.cs index c156153be5..6aed8a030c 100644 --- a/src/ImageSharp.Drawing/Processing/GradientRepetitionMode.cs +++ b/src/ImageSharp.Drawing/Processing/GradientRepetitionMode.cs @@ -21,16 +21,16 @@ namespace SixLabors.ImageSharp.Processing /// /// Reflect the gradient. - /// Similar to , but each other repetition uses inverse order of s. + /// Similar to , but each other repetition uses inverse order of s. /// Used on a Black-White gradient, Reflect leads to Black->{gray}->White->{gray}->White... /// Reflect, /// /// With DontFill a gradient does not touch any pixel beyond it's borders. - /// For the this is beyond the orthogonal through start and end, + /// For the this is beyond the orthogonal through start and end, /// TODO For the cref="PolygonalGradientBrush" it's outside the polygon, - /// For and it's beyond 1.0. + /// For and it's beyond 1.0. /// DontFill } diff --git a/src/ImageSharp.Drawing/Processing/IBrush.cs b/src/ImageSharp.Drawing/Processing/IBrush.cs index aa8c25b950..0cd2e20fda 100644 --- a/src/ImageSharp.Drawing/Processing/IBrush.cs +++ b/src/ImageSharp.Drawing/Processing/IBrush.cs @@ -9,16 +9,16 @@ namespace SixLabors.ImageSharp.Processing /// /// Brush represents a logical configuration of a brush which can be used to source pixel colors /// - /// The pixel format. /// /// A brush is a simple class that will return an that will perform the - /// logic for converting a pixel location to a . + /// logic for retrieving pixel values for specific locations. /// public interface IBrush { /// /// Creates the applicator for this brush. /// + /// The pixel type. /// The source image. /// The region the brush will be applied to. /// The graphic options diff --git a/src/ImageSharp.Drawing/Processing/IPen.cs b/src/ImageSharp.Drawing/Processing/IPen.cs index b9e359d601..af7517905d 100644 --- a/src/ImageSharp.Drawing/Processing/IPen.cs +++ b/src/ImageSharp.Drawing/Processing/IPen.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; + using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Processing @@ -15,7 +16,7 @@ namespace SixLabors.ImageSharp.Processing /// Gets the stroke fill. /// IBrush StrokeFill { get; } - + /// /// Gets the width to apply to the stroke /// @@ -26,4 +27,4 @@ namespace SixLabors.ImageSharp.Processing /// ReadOnlySpan StrokePattern { get; } } -} +} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/ImageBrush.cs b/src/ImageSharp.Drawing/Processing/ImageBrush.cs index c2a8d3a261..33f7b831ab 100644 --- a/src/ImageSharp.Drawing/Processing/ImageBrush.cs +++ b/src/ImageSharp.Drawing/Processing/ImageBrush.cs @@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Processing } specificImage = this.image.CloneAs(); - + return new ImageBrushApplicator(source, specificImage, region, options, true); } diff --git a/src/ImageSharp.Drawing/Processing/LinearGradientBrush.cs b/src/ImageSharp.Drawing/Processing/LinearGradientBrush.cs index fac43ad634..6eeadee5d6 100644 --- a/src/ImageSharp.Drawing/Processing/LinearGradientBrush.cs +++ b/src/ImageSharp.Drawing/Processing/LinearGradientBrush.cs @@ -14,7 +14,6 @@ namespace SixLabors.ImageSharp.Processing /// - a set of colors in relative distances to each other. /// public sealed class LinearGradientBrush : GradientBrushBase - { private readonly PointF p1; @@ -39,8 +38,17 @@ namespace SixLabors.ImageSharp.Processing } /// - public override BrushApplicator CreateApplicator(ImageFrame source, RectangleF region, GraphicsOptions options) - => new LinearGradientBrushApplicator(source, this.p1, this.p2, this.ColorStops, this.RepetitionMode, options); + public override BrushApplicator CreateApplicator( + ImageFrame source, + RectangleF region, + GraphicsOptions options) => + new LinearGradientBrushApplicator( + source, + this.p1, + this.p2, + this.ColorStops, + this.RepetitionMode, + options); /// /// The linear gradient brush applicator. @@ -137,9 +145,7 @@ namespace SixLabors.ImageSharp.Processing float y4 = y + (k * this.alongX); // get distance from (x4,y4) to start - float distance = (float)Math.Sqrt( - Math.Pow(x4 - this.start.X, 2) - + Math.Pow(y4 - this.start.Y, 2)); + float distance = (float)Math.Sqrt(Math.Pow(x4 - this.start.X, 2) + Math.Pow(y4 - this.start.Y, 2)); // get and return ratio float ratio = distance / this.length; From 0a3fb502c179cd3b22af41a23a9f23153a9efe4d Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 14 May 2019 00:01:50 +0200 Subject: [PATCH 191/223] Rgba32.Definitions: use Color instead of NamedColors --- .../Rgba32.Definitions.cs | 284 +++++++++--------- 1 file changed, 142 insertions(+), 142 deletions(-) diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.Definitions.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.Definitions.cs index 032d0b5464..f9cc3256cd 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.Definitions.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.Definitions.cs @@ -11,711 +11,711 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// Represents a matching the W3C definition that has an hex value of #F0F8FF. /// - public static readonly Rgba32 AliceBlue = NamedColors.AliceBlue; + public static readonly Rgba32 AliceBlue = Color.AliceBlue; /// /// Represents a matching the W3C definition that has an hex value of #FAEBD7. /// - public static readonly Rgba32 AntiqueWhite = NamedColors.AntiqueWhite; + public static readonly Rgba32 AntiqueWhite = Color.AntiqueWhite; /// /// Represents a matching the W3C definition that has an hex value of #00FFFF. /// - public static readonly Rgba32 Aqua = NamedColors.Aqua; + public static readonly Rgba32 Aqua = Color.Aqua; /// /// Represents a matching the W3C definition that has an hex value of #7FFFD4. /// - public static readonly Rgba32 Aquamarine = NamedColors.Aquamarine; + public static readonly Rgba32 Aquamarine = Color.Aquamarine; /// /// Represents a matching the W3C definition that has an hex value of #F0FFFF. /// - public static readonly Rgba32 Azure = NamedColors.Azure; + public static readonly Rgba32 Azure = Color.Azure; /// /// Represents a matching the W3C definition that has an hex value of #F5F5DC. /// - public static readonly Rgba32 Beige = NamedColors.Beige; + public static readonly Rgba32 Beige = Color.Beige; /// /// Represents a matching the W3C definition that has an hex value of #FFE4C4. /// - public static readonly Rgba32 Bisque = NamedColors.Bisque; + public static readonly Rgba32 Bisque = Color.Bisque; /// /// Represents a matching the W3C definition that has an hex value of #000000. /// - public static readonly Rgba32 Black = NamedColors.Black; + public static readonly Rgba32 Black = Color.Black; /// /// Represents a matching the W3C definition that has an hex value of #FFEBCD. /// - public static readonly Rgba32 BlanchedAlmond = NamedColors.BlanchedAlmond; + public static readonly Rgba32 BlanchedAlmond = Color.BlanchedAlmond; /// /// Represents a matching the W3C definition that has an hex value of #0000FF. /// - public static readonly Rgba32 Blue = NamedColors.Blue; + public static readonly Rgba32 Blue = Color.Blue; /// /// Represents a matching the W3C definition that has an hex value of #8A2BE2. /// - public static readonly Rgba32 BlueViolet = NamedColors.BlueViolet; + public static readonly Rgba32 BlueViolet = Color.BlueViolet; /// /// Represents a matching the W3C definition that has an hex value of #A52A2A. /// - public static readonly Rgba32 Brown = NamedColors.Brown; + public static readonly Rgba32 Brown = Color.Brown; /// /// Represents a matching the W3C definition that has an hex value of #DEB887. /// - public static readonly Rgba32 BurlyWood = NamedColors.BurlyWood; + public static readonly Rgba32 BurlyWood = Color.BurlyWood; /// /// Represents a matching the W3C definition that has an hex value of #5F9EA0. /// - public static readonly Rgba32 CadetBlue = NamedColors.CadetBlue; + public static readonly Rgba32 CadetBlue = Color.CadetBlue; /// /// Represents a matching the W3C definition that has an hex value of #7FFF00. /// - public static readonly Rgba32 Chartreuse = NamedColors.Chartreuse; + public static readonly Rgba32 Chartreuse = Color.Chartreuse; /// /// Represents a matching the W3C definition that has an hex value of #D2691E. /// - public static readonly Rgba32 Chocolate = NamedColors.Chocolate; + public static readonly Rgba32 Chocolate = Color.Chocolate; /// /// Represents a matching the W3C definition that has an hex value of #FF7F50. /// - public static readonly Rgba32 Coral = NamedColors.Coral; + public static readonly Rgba32 Coral = Color.Coral; /// /// Represents a matching the W3C definition that has an hex value of #6495ED. /// - public static readonly Rgba32 CornflowerBlue = NamedColors.CornflowerBlue; + public static readonly Rgba32 CornflowerBlue = Color.CornflowerBlue; /// /// Represents a matching the W3C definition that has an hex value of #FFF8DC. /// - public static readonly Rgba32 Cornsilk = NamedColors.Cornsilk; + public static readonly Rgba32 Cornsilk = Color.Cornsilk; /// /// Represents a matching the W3C definition that has an hex value of #DC143C. /// - public static readonly Rgba32 Crimson = NamedColors.Crimson; + public static readonly Rgba32 Crimson = Color.Crimson; /// /// Represents a matching the W3C definition that has an hex value of #00FFFF. /// - public static readonly Rgba32 Cyan = NamedColors.Cyan; + public static readonly Rgba32 Cyan = Color.Cyan; /// /// Represents a matching the W3C definition that has an hex value of #00008B. /// - public static readonly Rgba32 DarkBlue = NamedColors.DarkBlue; + public static readonly Rgba32 DarkBlue = Color.DarkBlue; /// /// Represents a matching the W3C definition that has an hex value of #008B8B. /// - public static readonly Rgba32 DarkCyan = NamedColors.DarkCyan; + public static readonly Rgba32 DarkCyan = Color.DarkCyan; /// /// Represents a matching the W3C definition that has an hex value of #B8860B. /// - public static readonly Rgba32 DarkGoldenrod = NamedColors.DarkGoldenrod; + public static readonly Rgba32 DarkGoldenrod = Color.DarkGoldenrod; /// /// Represents a matching the W3C definition that has an hex value of #A9A9A9. /// - public static readonly Rgba32 DarkGray = NamedColors.DarkGray; + public static readonly Rgba32 DarkGray = Color.DarkGray; /// /// Represents a matching the W3C definition that has an hex value of #006400. /// - public static readonly Rgba32 DarkGreen = NamedColors.DarkGreen; + public static readonly Rgba32 DarkGreen = Color.DarkGreen; /// /// Represents a matching the W3C definition that has an hex value of #BDB76B. /// - public static readonly Rgba32 DarkKhaki = NamedColors.DarkKhaki; + public static readonly Rgba32 DarkKhaki = Color.DarkKhaki; /// /// Represents a matching the W3C definition that has an hex value of #8B008B. /// - public static readonly Rgba32 DarkMagenta = NamedColors.DarkMagenta; + public static readonly Rgba32 DarkMagenta = Color.DarkMagenta; /// /// Represents a matching the W3C definition that has an hex value of #556B2F. /// - public static readonly Rgba32 DarkOliveGreen = NamedColors.DarkOliveGreen; + public static readonly Rgba32 DarkOliveGreen = Color.DarkOliveGreen; /// /// Represents a matching the W3C definition that has an hex value of #FF8C00. /// - public static readonly Rgba32 DarkOrange = NamedColors.DarkOrange; + public static readonly Rgba32 DarkOrange = Color.DarkOrange; /// /// Represents a matching the W3C definition that has an hex value of #9932CC. /// - public static readonly Rgba32 DarkOrchid = NamedColors.DarkOrchid; + public static readonly Rgba32 DarkOrchid = Color.DarkOrchid; /// /// Represents a matching the W3C definition that has an hex value of #8B0000. /// - public static readonly Rgba32 DarkRed = NamedColors.DarkRed; + public static readonly Rgba32 DarkRed = Color.DarkRed; /// /// Represents a matching the W3C definition that has an hex value of #E9967A. /// - public static readonly Rgba32 DarkSalmon = NamedColors.DarkSalmon; + public static readonly Rgba32 DarkSalmon = Color.DarkSalmon; /// /// Represents a matching the W3C definition that has an hex value of #8FBC8B. /// - public static readonly Rgba32 DarkSeaGreen = NamedColors.DarkSeaGreen; + public static readonly Rgba32 DarkSeaGreen = Color.DarkSeaGreen; /// /// Represents a matching the W3C definition that has an hex value of #483D8B. /// - public static readonly Rgba32 DarkSlateBlue = NamedColors.DarkSlateBlue; + public static readonly Rgba32 DarkSlateBlue = Color.DarkSlateBlue; /// /// Represents a matching the W3C definition that has an hex value of #2F4F4F. /// - public static readonly Rgba32 DarkSlateGray = NamedColors.DarkSlateGray; + public static readonly Rgba32 DarkSlateGray = Color.DarkSlateGray; /// /// Represents a matching the W3C definition that has an hex value of #00CED1. /// - public static readonly Rgba32 DarkTurquoise = NamedColors.DarkTurquoise; + public static readonly Rgba32 DarkTurquoise = Color.DarkTurquoise; /// /// Represents a matching the W3C definition that has an hex value of #9400D3. /// - public static readonly Rgba32 DarkViolet = NamedColors.DarkViolet; + public static readonly Rgba32 DarkViolet = Color.DarkViolet; /// /// Represents a matching the W3C definition that has an hex value of #FF1493. /// - public static readonly Rgba32 DeepPink = NamedColors.DeepPink; + public static readonly Rgba32 DeepPink = Color.DeepPink; /// /// Represents a matching the W3C definition that has an hex value of #00BFFF. /// - public static readonly Rgba32 DeepSkyBlue = NamedColors.DeepSkyBlue; + public static readonly Rgba32 DeepSkyBlue = Color.DeepSkyBlue; /// /// Represents a matching the W3C definition that has an hex value of #696969. /// - public static readonly Rgba32 DimGray = NamedColors.DimGray; + public static readonly Rgba32 DimGray = Color.DimGray; /// /// Represents a matching the W3C definition that has an hex value of #1E90FF. /// - public static readonly Rgba32 DodgerBlue = NamedColors.DodgerBlue; + public static readonly Rgba32 DodgerBlue = Color.DodgerBlue; /// /// Represents a matching the W3C definition that has an hex value of #B22222. /// - public static readonly Rgba32 Firebrick = NamedColors.Firebrick; + public static readonly Rgba32 Firebrick = Color.Firebrick; /// /// Represents a matching the W3C definition that has an hex value of #FFFAF0. /// - public static readonly Rgba32 FloralWhite = NamedColors.FloralWhite; + public static readonly Rgba32 FloralWhite = Color.FloralWhite; /// /// Represents a matching the W3C definition that has an hex value of #228B22. /// - public static readonly Rgba32 ForestGreen = NamedColors.ForestGreen; + public static readonly Rgba32 ForestGreen = Color.ForestGreen; /// /// Represents a matching the W3C definition that has an hex value of #FF00FF. /// - public static readonly Rgba32 Fuchsia = NamedColors.Fuchsia; + public static readonly Rgba32 Fuchsia = Color.Fuchsia; /// /// Represents a matching the W3C definition that has an hex value of #DCDCDC. /// - public static readonly Rgba32 Gainsboro = NamedColors.Gainsboro; + public static readonly Rgba32 Gainsboro = Color.Gainsboro; /// /// Represents a matching the W3C definition that has an hex value of #F8F8FF. /// - public static readonly Rgba32 GhostWhite = NamedColors.GhostWhite; + public static readonly Rgba32 GhostWhite = Color.GhostWhite; /// /// Represents a matching the W3C definition that has an hex value of #FFD700. /// - public static readonly Rgba32 Gold = NamedColors.Gold; + public static readonly Rgba32 Gold = Color.Gold; /// /// Represents a matching the W3C definition that has an hex value of #DAA520. /// - public static readonly Rgba32 Goldenrod = NamedColors.Goldenrod; + public static readonly Rgba32 Goldenrod = Color.Goldenrod; /// /// Represents a matching the W3C definition that has an hex value of #808080. /// - public static readonly Rgba32 Gray = NamedColors.Gray; + public static readonly Rgba32 Gray = Color.Gray; /// /// Represents a matching the W3C definition that has an hex value of #008000. /// - public static readonly Rgba32 Green = NamedColors.Green; + public static readonly Rgba32 Green = Color.Green; /// /// Represents a matching the W3C definition that has an hex value of #ADFF2F. /// - public static readonly Rgba32 GreenYellow = NamedColors.GreenYellow; + public static readonly Rgba32 GreenYellow = Color.GreenYellow; /// /// Represents a matching the W3C definition that has an hex value of #F0FFF0. /// - public static readonly Rgba32 Honeydew = NamedColors.Honeydew; + public static readonly Rgba32 Honeydew = Color.Honeydew; /// /// Represents a matching the W3C definition that has an hex value of #FF69B4. /// - public static readonly Rgba32 HotPink = NamedColors.HotPink; + public static readonly Rgba32 HotPink = Color.HotPink; /// /// Represents a matching the W3C definition that has an hex value of #CD5C5C. /// - public static readonly Rgba32 IndianRed = NamedColors.IndianRed; + public static readonly Rgba32 IndianRed = Color.IndianRed; /// /// Represents a matching the W3C definition that has an hex value of #4B0082. /// - public static readonly Rgba32 Indigo = NamedColors.Indigo; + public static readonly Rgba32 Indigo = Color.Indigo; /// /// Represents a matching the W3C definition that has an hex value of #FFFFF0. /// - public static readonly Rgba32 Ivory = NamedColors.Ivory; + public static readonly Rgba32 Ivory = Color.Ivory; /// /// Represents a matching the W3C definition that has an hex value of #F0E68C. /// - public static readonly Rgba32 Khaki = NamedColors.Khaki; + public static readonly Rgba32 Khaki = Color.Khaki; /// /// Represents a matching the W3C definition that has an hex value of #E6E6FA. /// - public static readonly Rgba32 Lavender = NamedColors.Lavender; + public static readonly Rgba32 Lavender = Color.Lavender; /// /// Represents a matching the W3C definition that has an hex value of #FFF0F5. /// - public static readonly Rgba32 LavenderBlush = NamedColors.LavenderBlush; + public static readonly Rgba32 LavenderBlush = Color.LavenderBlush; /// /// Represents a matching the W3C definition that has an hex value of #7CFC00. /// - public static readonly Rgba32 LawnGreen = NamedColors.LawnGreen; + public static readonly Rgba32 LawnGreen = Color.LawnGreen; /// /// Represents a matching the W3C definition that has an hex value of #FFFACD. /// - public static readonly Rgba32 LemonChiffon = NamedColors.LemonChiffon; + public static readonly Rgba32 LemonChiffon = Color.LemonChiffon; /// /// Represents a matching the W3C definition that has an hex value of #ADD8E6. /// - public static readonly Rgba32 LightBlue = NamedColors.LightBlue; + public static readonly Rgba32 LightBlue = Color.LightBlue; /// /// Represents a matching the W3C definition that has an hex value of #F08080. /// - public static readonly Rgba32 LightCoral = NamedColors.LightCoral; + public static readonly Rgba32 LightCoral = Color.LightCoral; /// /// Represents a matching the W3C definition that has an hex value of #E0FFFF. /// - public static readonly Rgba32 LightCyan = NamedColors.LightCyan; + public static readonly Rgba32 LightCyan = Color.LightCyan; /// /// Represents a matching the W3C definition that has an hex value of #FAFAD2. /// - public static readonly Rgba32 LightGoldenrodYellow = NamedColors.LightGoldenrodYellow; + public static readonly Rgba32 LightGoldenrodYellow = Color.LightGoldenrodYellow; /// /// Represents a matching the W3C definition that has an hex value of #D3D3D3. /// - public static readonly Rgba32 LightGray = NamedColors.LightGray; + public static readonly Rgba32 LightGray = Color.LightGray; /// /// Represents a matching the W3C definition that has an hex value of #90EE90. /// - public static readonly Rgba32 LightGreen = NamedColors.LightGreen; + public static readonly Rgba32 LightGreen = Color.LightGreen; /// /// Represents a matching the W3C definition that has an hex value of #FFB6C1. /// - public static readonly Rgba32 LightPink = NamedColors.LightPink; + public static readonly Rgba32 LightPink = Color.LightPink; /// /// Represents a matching the W3C definition that has an hex value of #FFA07A. /// - public static readonly Rgba32 LightSalmon = NamedColors.LightSalmon; + public static readonly Rgba32 LightSalmon = Color.LightSalmon; /// /// Represents a matching the W3C definition that has an hex value of #20B2AA. /// - public static readonly Rgba32 LightSeaGreen = NamedColors.LightSeaGreen; + public static readonly Rgba32 LightSeaGreen = Color.LightSeaGreen; /// /// Represents a matching the W3C definition that has an hex value of #87CEFA. /// - public static readonly Rgba32 LightSkyBlue = NamedColors.LightSkyBlue; + public static readonly Rgba32 LightSkyBlue = Color.LightSkyBlue; /// /// Represents a matching the W3C definition that has an hex value of #778899. /// - public static readonly Rgba32 LightSlateGray = NamedColors.LightSlateGray; + public static readonly Rgba32 LightSlateGray = Color.LightSlateGray; /// /// Represents a matching the W3C definition that has an hex value of #B0C4DE. /// - public static readonly Rgba32 LightSteelBlue = NamedColors.LightSteelBlue; + public static readonly Rgba32 LightSteelBlue = Color.LightSteelBlue; /// /// Represents a matching the W3C definition that has an hex value of #FFFFE0. /// - public static readonly Rgba32 LightYellow = NamedColors.LightYellow; + public static readonly Rgba32 LightYellow = Color.LightYellow; /// /// Represents a matching the W3C definition that has an hex value of #00FF00. /// - public static readonly Rgba32 Lime = NamedColors.Lime; + public static readonly Rgba32 Lime = Color.Lime; /// /// Represents a matching the W3C definition that has an hex value of #32CD32. /// - public static readonly Rgba32 LimeGreen = NamedColors.LimeGreen; + public static readonly Rgba32 LimeGreen = Color.LimeGreen; /// /// Represents a matching the W3C definition that has an hex value of #FAF0E6. /// - public static readonly Rgba32 Linen = NamedColors.Linen; + public static readonly Rgba32 Linen = Color.Linen; /// /// Represents a matching the W3C definition that has an hex value of #FF00FF. /// - public static readonly Rgba32 Magenta = NamedColors.Magenta; + public static readonly Rgba32 Magenta = Color.Magenta; /// /// Represents a matching the W3C definition that has an hex value of #800000. /// - public static readonly Rgba32 Maroon = NamedColors.Maroon; + public static readonly Rgba32 Maroon = Color.Maroon; /// /// Represents a matching the W3C definition that has an hex value of #66CDAA. /// - public static readonly Rgba32 MediumAquamarine = NamedColors.MediumAquamarine; + public static readonly Rgba32 MediumAquamarine = Color.MediumAquamarine; /// /// Represents a matching the W3C definition that has an hex value of #0000CD. /// - public static readonly Rgba32 MediumBlue = NamedColors.MediumBlue; + public static readonly Rgba32 MediumBlue = Color.MediumBlue; /// /// Represents a matching the W3C definition that has an hex value of #BA55D3. /// - public static readonly Rgba32 MediumOrchid = NamedColors.MediumOrchid; + public static readonly Rgba32 MediumOrchid = Color.MediumOrchid; /// /// Represents a matching the W3C definition that has an hex value of #9370DB. /// - public static readonly Rgba32 MediumPurple = NamedColors.MediumPurple; + public static readonly Rgba32 MediumPurple = Color.MediumPurple; /// /// Represents a matching the W3C definition that has an hex value of #3CB371. /// - public static readonly Rgba32 MediumSeaGreen = NamedColors.MediumSeaGreen; + public static readonly Rgba32 MediumSeaGreen = Color.MediumSeaGreen; /// /// Represents a matching the W3C definition that has an hex value of #7B68EE. /// - public static readonly Rgba32 MediumSlateBlue = NamedColors.MediumSlateBlue; + public static readonly Rgba32 MediumSlateBlue = Color.MediumSlateBlue; /// /// Represents a matching the W3C definition that has an hex value of #00FA9A. /// - public static readonly Rgba32 MediumSpringGreen = NamedColors.MediumSpringGreen; + public static readonly Rgba32 MediumSpringGreen = Color.MediumSpringGreen; /// /// Represents a matching the W3C definition that has an hex value of #48D1CC. /// - public static readonly Rgba32 MediumTurquoise = NamedColors.MediumTurquoise; + public static readonly Rgba32 MediumTurquoise = Color.MediumTurquoise; /// /// Represents a matching the W3C definition that has an hex value of #C71585. /// - public static readonly Rgba32 MediumVioletRed = NamedColors.MediumVioletRed; + public static readonly Rgba32 MediumVioletRed = Color.MediumVioletRed; /// /// Represents a matching the W3C definition that has an hex value of #191970. /// - public static readonly Rgba32 MidnightBlue = NamedColors.MidnightBlue; + public static readonly Rgba32 MidnightBlue = Color.MidnightBlue; /// /// Represents a matching the W3C definition that has an hex value of #F5FFFA. /// - public static readonly Rgba32 MintCream = NamedColors.MintCream; + public static readonly Rgba32 MintCream = Color.MintCream; /// /// Represents a matching the W3C definition that has an hex value of #FFE4E1. /// - public static readonly Rgba32 MistyRose = NamedColors.MistyRose; + public static readonly Rgba32 MistyRose = Color.MistyRose; /// /// Represents a matching the W3C definition that has an hex value of #FFE4B5. /// - public static readonly Rgba32 Moccasin = NamedColors.Moccasin; + public static readonly Rgba32 Moccasin = Color.Moccasin; /// /// Represents a matching the W3C definition that has an hex value of #FFDEAD. /// - public static readonly Rgba32 NavajoWhite = NamedColors.NavajoWhite; + public static readonly Rgba32 NavajoWhite = Color.NavajoWhite; /// /// Represents a matching the W3C definition that has an hex value of #000080. /// - public static readonly Rgba32 Navy = NamedColors.Navy; + public static readonly Rgba32 Navy = Color.Navy; /// /// Represents a matching the W3C definition that has an hex value of #FDF5E6. /// - public static readonly Rgba32 OldLace = NamedColors.OldLace; + public static readonly Rgba32 OldLace = Color.OldLace; /// /// Represents a matching the W3C definition that has an hex value of #808000. /// - public static readonly Rgba32 Olive = NamedColors.Olive; + public static readonly Rgba32 Olive = Color.Olive; /// /// Represents a matching the W3C definition that has an hex value of #6B8E23. /// - public static readonly Rgba32 OliveDrab = NamedColors.OliveDrab; + public static readonly Rgba32 OliveDrab = Color.OliveDrab; /// /// Represents a matching the W3C definition that has an hex value of #FFA500. /// - public static readonly Rgba32 Orange = NamedColors.Orange; + public static readonly Rgba32 Orange = Color.Orange; /// /// Represents a matching the W3C definition that has an hex value of #FF4500. /// - public static readonly Rgba32 OrangeRed = NamedColors.OrangeRed; + public static readonly Rgba32 OrangeRed = Color.OrangeRed; /// /// Represents a matching the W3C definition that has an hex value of #DA70D6. /// - public static readonly Rgba32 Orchid = NamedColors.Orchid; + public static readonly Rgba32 Orchid = Color.Orchid; /// /// Represents a matching the W3C definition that has an hex value of #EEE8AA. /// - public static readonly Rgba32 PaleGoldenrod = NamedColors.PaleGoldenrod; + public static readonly Rgba32 PaleGoldenrod = Color.PaleGoldenrod; /// /// Represents a matching the W3C definition that has an hex value of #98FB98. /// - public static readonly Rgba32 PaleGreen = NamedColors.PaleGreen; + public static readonly Rgba32 PaleGreen = Color.PaleGreen; /// /// Represents a matching the W3C definition that has an hex value of #AFEEEE. /// - public static readonly Rgba32 PaleTurquoise = NamedColors.PaleTurquoise; + public static readonly Rgba32 PaleTurquoise = Color.PaleTurquoise; /// /// Represents a matching the W3C definition that has an hex value of #DB7093. /// - public static readonly Rgba32 PaleVioletRed = NamedColors.PaleVioletRed; + public static readonly Rgba32 PaleVioletRed = Color.PaleVioletRed; /// /// Represents a matching the W3C definition that has an hex value of #FFEFD5. /// - public static readonly Rgba32 PapayaWhip = NamedColors.PapayaWhip; + public static readonly Rgba32 PapayaWhip = Color.PapayaWhip; /// /// Represents a matching the W3C definition that has an hex value of #FFDAB9. /// - public static readonly Rgba32 PeachPuff = NamedColors.PeachPuff; + public static readonly Rgba32 PeachPuff = Color.PeachPuff; /// /// Represents a matching the W3C definition that has an hex value of #CD853F. /// - public static readonly Rgba32 Peru = NamedColors.Peru; + public static readonly Rgba32 Peru = Color.Peru; /// /// Represents a matching the W3C definition that has an hex value of #FFC0CB. /// - public static readonly Rgba32 Pink = NamedColors.Pink; + public static readonly Rgba32 Pink = Color.Pink; /// /// Represents a matching the W3C definition that has an hex value of #DDA0DD. /// - public static readonly Rgba32 Plum = NamedColors.Plum; + public static readonly Rgba32 Plum = Color.Plum; /// /// Represents a matching the W3C definition that has an hex value of #B0E0E6. /// - public static readonly Rgba32 PowderBlue = NamedColors.PowderBlue; + public static readonly Rgba32 PowderBlue = Color.PowderBlue; /// /// Represents a matching the W3C definition that has an hex value of #800080. /// - public static readonly Rgba32 Purple = NamedColors.Purple; + public static readonly Rgba32 Purple = Color.Purple; /// /// Represents a matching the W3C definition that has an hex value of #663399. /// - public static readonly Rgba32 RebeccaPurple = NamedColors.RebeccaPurple; + public static readonly Rgba32 RebeccaPurple = Color.RebeccaPurple; /// /// Represents a matching the W3C definition that has an hex value of #FF0000. /// - public static readonly Rgba32 Red = NamedColors.Red; + public static readonly Rgba32 Red = Color.Red; /// /// Represents a matching the W3C definition that has an hex value of #BC8F8F. /// - public static readonly Rgba32 RosyBrown = NamedColors.RosyBrown; + public static readonly Rgba32 RosyBrown = Color.RosyBrown; /// /// Represents a matching the W3C definition that has an hex value of #4169E1. /// - public static readonly Rgba32 RoyalBlue = NamedColors.RoyalBlue; + public static readonly Rgba32 RoyalBlue = Color.RoyalBlue; /// /// Represents a matching the W3C definition that has an hex value of #8B4513. /// - public static readonly Rgba32 SaddleBrown = NamedColors.SaddleBrown; + public static readonly Rgba32 SaddleBrown = Color.SaddleBrown; /// /// Represents a matching the W3C definition that has an hex value of #FA8072. /// - public static readonly Rgba32 Salmon = NamedColors.Salmon; + public static readonly Rgba32 Salmon = Color.Salmon; /// /// Represents a matching the W3C definition that has an hex value of #F4A460. /// - public static readonly Rgba32 SandyBrown = NamedColors.SandyBrown; + public static readonly Rgba32 SandyBrown = Color.SandyBrown; /// /// Represents a matching the W3C definition that has an hex value of #2E8B57. /// - public static readonly Rgba32 SeaGreen = NamedColors.SeaGreen; + public static readonly Rgba32 SeaGreen = Color.SeaGreen; /// /// Represents a matching the W3C definition that has an hex value of #FFF5EE. /// - public static readonly Rgba32 SeaShell = NamedColors.SeaShell; + public static readonly Rgba32 SeaShell = Color.SeaShell; /// /// Represents a matching the W3C definition that has an hex value of #A0522D. /// - public static readonly Rgba32 Sienna = NamedColors.Sienna; + public static readonly Rgba32 Sienna = Color.Sienna; /// /// Represents a matching the W3C definition that has an hex value of #C0C0C0. /// - public static readonly Rgba32 Silver = NamedColors.Silver; + public static readonly Rgba32 Silver = Color.Silver; /// /// Represents a matching the W3C definition that has an hex value of #87CEEB. /// - public static readonly Rgba32 SkyBlue = NamedColors.SkyBlue; + public static readonly Rgba32 SkyBlue = Color.SkyBlue; /// /// Represents a matching the W3C definition that has an hex value of #6A5ACD. /// - public static readonly Rgba32 SlateBlue = NamedColors.SlateBlue; + public static readonly Rgba32 SlateBlue = Color.SlateBlue; /// /// Represents a matching the W3C definition that has an hex value of #708090. /// - public static readonly Rgba32 SlateGray = NamedColors.SlateGray; + public static readonly Rgba32 SlateGray = Color.SlateGray; /// /// Represents a matching the W3C definition that has an hex value of #FFFAFA. /// - public static readonly Rgba32 Snow = NamedColors.Snow; + public static readonly Rgba32 Snow = Color.Snow; /// /// Represents a matching the W3C definition that has an hex value of #00FF7F. /// - public static readonly Rgba32 SpringGreen = NamedColors.SpringGreen; + public static readonly Rgba32 SpringGreen = Color.SpringGreen; /// /// Represents a matching the W3C definition that has an hex value of #4682B4. /// - public static readonly Rgba32 SteelBlue = NamedColors.SteelBlue; + public static readonly Rgba32 SteelBlue = Color.SteelBlue; /// /// Represents a matching the W3C definition that has an hex value of #D2B48C. /// - public static readonly Rgba32 Tan = NamedColors.Tan; + public static readonly Rgba32 Tan = Color.Tan; /// /// Represents a matching the W3C definition that has an hex value of #008080. /// - public static readonly Rgba32 Teal = NamedColors.Teal; + public static readonly Rgba32 Teal = Color.Teal; /// /// Represents a matching the W3C definition that has an hex value of #D8BFD8. /// - public static readonly Rgba32 Thistle = NamedColors.Thistle; + public static readonly Rgba32 Thistle = Color.Thistle; /// /// Represents a matching the W3C definition that has an hex value of #FF6347. /// - public static readonly Rgba32 Tomato = NamedColors.Tomato; + public static readonly Rgba32 Tomato = Color.Tomato; /// /// Represents a matching the W3C definition that has an hex value of #FFFFFF. /// - public static readonly Rgba32 Transparent = NamedColors.Transparent; + public static readonly Rgba32 Transparent = Color.Transparent; /// /// Represents a matching the W3C definition that has an hex value of #40E0D0. /// - public static readonly Rgba32 Turquoise = NamedColors.Turquoise; + public static readonly Rgba32 Turquoise = Color.Turquoise; /// /// Represents a matching the W3C definition that has an hex value of #EE82EE. /// - public static readonly Rgba32 Violet = NamedColors.Violet; + public static readonly Rgba32 Violet = Color.Violet; /// /// Represents a matching the W3C definition that has an hex value of #F5DEB3. /// - public static readonly Rgba32 Wheat = NamedColors.Wheat; + public static readonly Rgba32 Wheat = Color.Wheat; /// /// Represents a matching the W3C definition that has an hex value of #FFFFFF. /// - public static readonly Rgba32 White = NamedColors.White; + public static readonly Rgba32 White = Color.White; /// /// Represents a matching the W3C definition that has an hex value of #F5F5F5. /// - public static readonly Rgba32 WhiteSmoke = NamedColors.WhiteSmoke; + public static readonly Rgba32 WhiteSmoke = Color.WhiteSmoke; /// /// Represents a matching the W3C definition that has an hex value of #FFFF00. /// - public static readonly Rgba32 Yellow = NamedColors.Yellow; + public static readonly Rgba32 Yellow = Color.Yellow; /// /// Represents a matching the W3C definition that has an hex value of #9ACD32. /// - public static readonly Rgba32 YellowGreen = NamedColors.YellowGreen; + public static readonly Rgba32 YellowGreen = Color.YellowGreen; } } \ No newline at end of file From 85147d26dceb3807b780060e4ef169b89b62cdbd Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 14 May 2019 00:06:17 +0200 Subject: [PATCH 192/223] Remove NamedColors usages --- src/ImageSharp.Drawing/Processing/GradientBrushBase.cs | 4 +++- .../Attributes/WithSolidFilledImagesAttribute.cs | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/ImageSharp.Drawing/Processing/GradientBrushBase.cs b/src/ImageSharp.Drawing/Processing/GradientBrushBase.cs index 6e1e0086da..39dbcb61a5 100644 --- a/src/ImageSharp.Drawing/Processing/GradientBrushBase.cs +++ b/src/ImageSharp.Drawing/Processing/GradientBrushBase.cs @@ -49,6 +49,8 @@ namespace SixLabors.ImageSharp.Processing internal abstract class GradientBrushApplicatorBase : BrushApplicator where TPixel : struct, IPixel { + private static readonly TPixel Transparent = Color.Transparent.ToPixel(); + private readonly ColorStop[] colorStops; private readonly GradientRepetitionMode repetitionMode; @@ -103,7 +105,7 @@ namespace SixLabors.ImageSharp.Processing case GradientRepetitionMode.DontFill: if (positionOnCompleteGradient > 1 || positionOnCompleteGradient < 0) { - return NamedColors.Transparent; + return Transparent; } break; diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithSolidFilledImagesAttribute.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithSolidFilledImagesAttribute.cs index f95db45f71..190e80fbad 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithSolidFilledImagesAttribute.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithSolidFilledImagesAttribute.cs @@ -99,7 +99,7 @@ namespace SixLabors.ImageSharp.Tests /// /// The width of the requested image /// The height of the requested image - /// The referenced color name (name of property in + /// The referenced color name (name of property in ). /// The requested pixel types /// Additional theory parameter values public WithSolidFilledImagesAttribute( @@ -119,7 +119,7 @@ namespace SixLabors.ImageSharp.Tests /// The member data to apply to theories /// The width of the requested image /// The height of the requested image - /// The referenced color name (name of property in + /// The referenced color name (name of property in ). /// The requested pixel types /// Additional theory parameter values public WithSolidFilledImagesAttribute( From 028b6b5b7ef046dedc4bb2b5c6be36aaf531591e Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 15 May 2019 21:15:17 +0200 Subject: [PATCH 193/223] Not using quantization for Grey8 images when encoding 8Bit Bitmaps --- src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs | 83 ++++++++++++++----- .../Formats/Bmp/BmpEncoderTests.cs | 6 +- 2 files changed, 62 insertions(+), 27 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index edde7dc893..532d20e000 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -305,37 +305,74 @@ namespace SixLabors.ImageSharp.Formats.Bmp private void Write8Bit(Stream stream, ImageFrame image) where TPixel : struct, IPixel { + bool isGray8 = typeof(TPixel) == typeof(Gray8); using (IMemoryOwner colorPaletteBuffer = this.memoryAllocator.AllocateManagedByteBuffer(ColorPaletteSize8Bit, AllocationOptions.Clean)) - using (IQuantizedFrame quantized = this.quantizer.CreateFrameQuantizer(this.configuration, 256).QuantizeFrame(image)) { Span colorPalette = colorPaletteBuffer.GetSpan(); - int idx = 0; - var color = default(Rgba32); - ReadOnlySpan paletteSpan = quantized.Palette.Span; - - // TODO: Use bulk conversion here for better perf - foreach (TPixel quantizedColor in paletteSpan) + if (isGray8) { - quantizedColor.ToRgba32(ref color); - colorPalette[idx] = color.B; - colorPalette[idx + 1] = color.G; - colorPalette[idx + 2] = color.R; - - // Padding byte, always 0 - colorPalette[idx + 3] = 0; - idx += 4; - } + for (byte i = 0; i <= 255; i++) + { + int idx = i * 4; + colorPalette[idx] = i; + colorPalette[idx + 1] = i; + colorPalette[idx + 2] = i; - stream.Write(colorPalette); + // Padding byte, always 0 + colorPalette[idx + 3] = 0; + } - for (int y = image.Height - 1; y >= 0; y--) - { - ReadOnlySpan pixelSpan = quantized.GetRowSpan(y); - stream.Write(pixelSpan); + stream.Write(colorPalette); - for (int i = 0; i < this.padding; i++) + using (IMemoryOwner rowSpanBuffer = this.memoryAllocator.AllocateManagedByteBuffer(image.Width, AllocationOptions.Clean)) + { + Span outputPixelRow = rowSpanBuffer.GetSpan(); + for (int y = image.Height - 1; y >= 0; y--) + { + Span inputPixelRow = image.GetPixelRowSpan(y); + PixelOperations.Instance.ToGray8Bytes(this.configuration, inputPixelRow, outputPixelRow, image.Width); + stream.Write(outputPixelRow); + + for (int i = 0; i < this.padding; i++) + { + stream.WriteByte(0); + } + } + } + } + else + { + using (IQuantizedFrame quantized = this.quantizer.CreateFrameQuantizer(this.configuration, 256).QuantizeFrame(image)) { - stream.WriteByte(0); + ReadOnlySpan quantizedColors = quantized.Palette.Span; + var color = default(Rgba32); + + // TODO: Use bulk conversion here for better perf + int idx = 0; + foreach (TPixel quantizedColor in quantizedColors) + { + quantizedColor.ToRgba32(ref color); + colorPalette[idx] = color.B; + colorPalette[idx + 1] = color.G; + colorPalette[idx + 2] = color.R; + + // Padding byte, always 0 + colorPalette[idx + 3] = 0; + idx += 4; + } + + stream.Write(colorPalette); + + for (int y = image.Height - 1; y >= 0; y--) + { + ReadOnlySpan pixelSpan = quantized.GetRowSpan(y); + stream.Write(pixelSpan); + + for (int i = 0; i < this.padding; i++) + { + stream.WriteByte(0); + } + } } } } diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs index dd76e9443c..162d3f30b9 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs @@ -169,8 +169,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp TestBmpEncoderCore( provider, bitsPerPixel, - supportTransparency: false, - ImageComparer.TolerantPercentage(0.01f)); + supportTransparency: false); [Theory] [WithFile(Bit8Gs, PixelTypes.Gray8, BmpBitsPerPixel.Pixel8)] @@ -179,8 +178,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp TestBmpEncoderCore( provider, bitsPerPixel, - supportTransparency: true, - ImageComparer.TolerantPercentage(0.01f)); + supportTransparency: true); [Theory] [WithFile(TestImages.Png.GrayAlpha2BitInterlaced, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel32)] From e8b29a2e98ecb2e4db78f77f9cd007d2d60c11c3 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Thu, 16 May 2019 20:08:16 +0200 Subject: [PATCH 194/223] Refactor Write8Bit into two methods: one for gray and one for color --- src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs | 140 ++++++++++++------- 1 file changed, 86 insertions(+), 54 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index 532d20e000..e9600f640d 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -311,68 +311,100 @@ namespace SixLabors.ImageSharp.Formats.Bmp Span colorPalette = colorPaletteBuffer.GetSpan(); if (isGray8) { - for (byte i = 0; i <= 255; i++) - { - int idx = i * 4; - colorPalette[idx] = i; - colorPalette[idx + 1] = i; - colorPalette[idx + 2] = i; + this.Write8BitGray(stream, image, colorPalette); + } + else + { + this.Write8BitColor(stream, image, colorPalette); + } + } + } - // Padding byte, always 0 - colorPalette[idx + 3] = 0; - } + /// + /// Writes an 8 Bit color image with a color palette. The color palette has 256 entry's with 4 bytes for each entry. + /// + /// The type of the pixel. + /// The to write to. + /// The containing pixel data. + /// A byte span of size 1024 for the color palette. + private void Write8BitColor(Stream stream, ImageFrame image, Span colorPalette) + where TPixel : struct, IPixel + { + using (IQuantizedFrame quantized = this.quantizer.CreateFrameQuantizer(this.configuration, 256).QuantizeFrame(image)) + { + ReadOnlySpan quantizedColors = quantized.Palette.Span; + var color = default(Rgba32); - stream.Write(colorPalette); + // TODO: Use bulk conversion here for better perf + int idx = 0; + foreach (TPixel quantizedColor in quantizedColors) + { + quantizedColor.ToRgba32(ref color); + colorPalette[idx] = color.B; + colorPalette[idx + 1] = color.G; + colorPalette[idx + 2] = color.R; + + // Padding byte, always 0. + colorPalette[idx + 3] = 0; + idx += 4; + } - using (IMemoryOwner rowSpanBuffer = this.memoryAllocator.AllocateManagedByteBuffer(image.Width, AllocationOptions.Clean)) + stream.Write(colorPalette); + + for (int y = image.Height - 1; y >= 0; y--) + { + ReadOnlySpan pixelSpan = quantized.GetRowSpan(y); + stream.Write(pixelSpan); + + for (int i = 0; i < this.padding; i++) { - Span outputPixelRow = rowSpanBuffer.GetSpan(); - for (int y = image.Height - 1; y >= 0; y--) - { - Span inputPixelRow = image.GetPixelRowSpan(y); - PixelOperations.Instance.ToGray8Bytes(this.configuration, inputPixelRow, outputPixelRow, image.Width); - stream.Write(outputPixelRow); - - for (int i = 0; i < this.padding; i++) - { - stream.WriteByte(0); - } - } + stream.WriteByte(0); } } - else + } + } + + /// + /// Writes an 8 Bit gray image with a color palette. The color palette has 256 entry's with 4 bytes for each entry. + /// + /// The type of the pixel. + /// The to write to. + /// The containing pixel data. + /// A byte span of size 1024 for the color palette. + private void Write8BitGray(Stream stream, ImageFrame image, Span colorPalette) + where TPixel : struct, IPixel + { + // Create a color palette with 256 different gray values. + for (int i = 0; i <= 255; i++) + { + int idx = i * 4; + byte grayValue = (byte)i; + colorPalette[idx] = grayValue; + colorPalette[idx + 1] = grayValue; + colorPalette[idx + 2] = grayValue; + + // Padding byte, always 0. + colorPalette[idx + 3] = 0; + } + + stream.Write(colorPalette); + + using (IMemoryOwner rowSpanBuffer = this.memoryAllocator.AllocateManagedByteBuffer(image.Width, AllocationOptions.Clean)) + { + Span outputPixelRow = rowSpanBuffer.GetSpan(); + for (int y = image.Height - 1; y >= 0; y--) { - using (IQuantizedFrame quantized = this.quantizer.CreateFrameQuantizer(this.configuration, 256).QuantizeFrame(image)) + Span inputPixelRow = image.GetPixelRowSpan(y); + PixelOperations.Instance.ToGray8Bytes( + this.configuration, + inputPixelRow, + outputPixelRow, + image.Width); + stream.Write(outputPixelRow); + + for (int i = 0; i < this.padding; i++) { - ReadOnlySpan quantizedColors = quantized.Palette.Span; - var color = default(Rgba32); - - // TODO: Use bulk conversion here for better perf - int idx = 0; - foreach (TPixel quantizedColor in quantizedColors) - { - quantizedColor.ToRgba32(ref color); - colorPalette[idx] = color.B; - colorPalette[idx + 1] = color.G; - colorPalette[idx + 2] = color.R; - - // Padding byte, always 0 - colorPalette[idx + 3] = 0; - idx += 4; - } - - stream.Write(colorPalette); - - for (int y = image.Height - 1; y >= 0; y--) - { - ReadOnlySpan pixelSpan = quantized.GetRowSpan(y); - stream.Write(pixelSpan); - - for (int i = 0; i < this.padding; i++) - { - stream.WriteByte(0); - } - } + stream.WriteByte(0); } } } From 9ea9f2df5d7ded528b342b62d3dcd42e6dfdaf1f Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 16 May 2019 23:38:28 +0200 Subject: [PATCH 195/223] drop unnecessary generic IImageProcessorContext usages --- .../Extensions/ProcessingExtensions.cs | 14 ++-- .../ImageSharp.Benchmarks/Drawing/DrawText.cs | 10 ++- .../Drawing/DrawTextOutline.cs | 5 +- .../ImageSharp.Benchmarks/Samplers/Resize.cs | 10 +-- tests/ImageSharp.Tests/ImageOperationTests.cs | 39 ++++++----- .../Basic1ParameterConvolutionTests.cs | 6 +- .../Processors/Convolution/BoxBlurTest.cs | 4 +- .../Convolution/GaussianBlurTest.cs | 4 +- .../Convolution/GaussianSharpenTest.cs | 4 +- .../Processors/Transforms/ResizeTests.cs | 6 +- .../ImageProviders/TestImageProvider.cs | 2 +- .../TestUtilities/TestImageExtensions.cs | 69 ++++++++++--------- .../TestUtilities/TestUtils.cs | 14 ++-- 13 files changed, 99 insertions(+), 88 deletions(-) diff --git a/src/ImageSharp/Processing/Extensions/ProcessingExtensions.cs b/src/ImageSharp/Processing/Extensions/ProcessingExtensions.cs index c72e8cfb77..e469af18b8 100644 --- a/src/ImageSharp/Processing/Extensions/ProcessingExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/ProcessingExtensions.cs @@ -42,7 +42,7 @@ namespace SixLabors.ImageSharp.Processing /// The pixel format. /// The image to mutate. /// The operation to perform on the source. - public static void Mutate(this Image source, Action> operation) + public static void Mutate(this Image source, Action operation) where TPixel : struct, IPixel { Guard.NotNull(operation, nameof(operation)); @@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.Processing /// The pixel format. /// The image to mutate. /// The operations to perform on the source. - public static void Mutate(this Image source, params IImageProcessor[] operations) + public static void Mutate(this Image source, params IImageProcessor[] operations) where TPixel : struct, IPixel { Guard.NotNull(operations, nameof(operations)); @@ -90,7 +90,7 @@ namespace SixLabors.ImageSharp.Processing /// The image to clone. /// The operation to perform on the clone. /// The new - public static Image Clone(this Image source, Action> operation) + public static Image Clone(this Image source, Action operation) where TPixel : struct, IPixel { Guard.NotNull(operation, nameof(operation)); @@ -108,7 +108,7 @@ namespace SixLabors.ImageSharp.Processing /// The image to clone. /// The operations to perform on the clone. /// The new - public static Image Clone(this Image source, params IImageProcessor[] operations) + public static Image Clone(this Image source, params IImageProcessor[] operations) where TPixel : struct, IPixel { Guard.NotNull(operations, nameof(operations)); @@ -122,14 +122,12 @@ namespace SixLabors.ImageSharp.Processing /// /// Applies the given collection against the context /// - /// The pixel format. /// The image processing context. /// The operations to perform on the source. /// The to allow chaining of operations. - public static IImageProcessingContext ApplyProcessors(this IImageProcessingContext source, params IImageProcessor[] operations) - where TPixel : struct, IPixel + public static IImageProcessingContext ApplyProcessors(this IImageProcessingContext source, params IImageProcessor[] operations) { - foreach (IImageProcessor p in operations) + foreach (IImageProcessor p in operations) { source = source.ApplyProcessor(p); } diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawText.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawText.cs index 28f410e7d1..0982db3340 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/DrawText.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/DrawText.cs @@ -55,8 +55,14 @@ namespace SixLabors.ImageSharp.Benchmarks image.Mutate(x => DrawTextOldVersion(x, new TextGraphicsOptions(true) { WrapTextWidth = 780 }, TextToRender, font, Processing.Brushes.Solid(Rgba32.HotPink), null, new SixLabors.Primitives.PointF(10, 10))); } - IImageProcessingContext DrawTextOldVersion(IImageProcessingContext source, TextGraphicsOptions options, string text, SixLabors.Fonts.Font font, IBrush brush, IPen pen, SixLabors.Primitives.PointF location) - where TPixel : struct, IPixel + IImageProcessingContext DrawTextOldVersion( + IImageProcessingContext source, + TextGraphicsOptions options, + string text, + SixLabors.Fonts.Font font, + IBrush brush, + IPen pen, + SixLabors.Primitives.PointF location) { float dpiX = 72; float dpiY = 72; diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawTextOutline.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawTextOutline.cs index 658590e2d4..c5c1ba5ac1 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/DrawTextOutline.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/DrawTextOutline.cs @@ -64,15 +64,14 @@ namespace SixLabors.ImageSharp.Benchmarks new SixLabors.Primitives.PointF(10, 10))); } - IImageProcessingContext DrawTextOldVersion( - IImageProcessingContext source, + IImageProcessingContext DrawTextOldVersion( + IImageProcessingContext source, TextGraphicsOptions options, string text, SixLabors.Fonts.Font font, IBrush brush, IPen pen, SixLabors.Primitives.PointF location) - where TPixel : struct, IPixel { var style = new SixLabors.Fonts.RendererOptions(font, options.DpiX, options.DpiY, location) { diff --git a/tests/ImageSharp.Benchmarks/Samplers/Resize.cs b/tests/ImageSharp.Benchmarks/Samplers/Resize.cs index cf47202cc8..172e243729 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/Resize.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/Resize.cs @@ -91,12 +91,12 @@ namespace SixLabors.ImageSharp.Benchmarks } } - protected abstract void ExecuteResizeOperation(IImageProcessingContext ctx); + protected abstract void ExecuteResizeOperation(IImageProcessingContext ctx); } public class Resize_Bicubic_Rgba32 : ResizeBenchmarkBase { - protected override void ExecuteResizeOperation(IImageProcessingContext ctx) + protected override void ExecuteResizeOperation(IImageProcessingContext ctx) { ctx.Resize(this.DestSize, this.DestSize, KnownResamplers.Bicubic); } @@ -143,7 +143,7 @@ namespace SixLabors.ImageSharp.Benchmarks public class Resize_Bicubic_Bgra32 : ResizeBenchmarkBase { - protected override void ExecuteResizeOperation(IImageProcessingContext ctx) + protected override void ExecuteResizeOperation(IImageProcessingContext ctx) { ctx.Resize(this.DestSize, this.DestSize, KnownResamplers.Bicubic); } @@ -171,7 +171,7 @@ namespace SixLabors.ImageSharp.Benchmarks public class Resize_Bicubic_Rgb24 : ResizeBenchmarkBase { - protected override void ExecuteResizeOperation(IImageProcessingContext ctx) + protected override void ExecuteResizeOperation(IImageProcessingContext ctx) { ctx.Resize(this.DestSize, this.DestSize, KnownResamplers.Bicubic); } @@ -198,7 +198,7 @@ namespace SixLabors.ImageSharp.Benchmarks public class Resize_BicubicCompand_Rgba32 : ResizeBenchmarkBase { - protected override void ExecuteResizeOperation(IImageProcessingContext ctx) + protected override void ExecuteResizeOperation(IImageProcessingContext ctx) { ctx.Resize(this.DestSize, this.DestSize, KnownResamplers.Bicubic, true); } diff --git a/tests/ImageSharp.Tests/ImageOperationTests.cs b/tests/ImageSharp.Tests/ImageOperationTests.cs index e694f0b644..b99ad64b24 100644 --- a/tests/ImageSharp.Tests/ImageOperationTests.cs +++ b/tests/ImageSharp.Tests/ImageOperationTests.cs @@ -21,14 +21,21 @@ namespace SixLabors.ImageSharp.Tests { private readonly Image image; private readonly FakeImageOperationsProvider provider; - private readonly IImageProcessor processor; + private readonly IImageProcessor processorImplementation; + + private readonly IImageProcessor processorDefinition; public Configuration Configuration { get; private set; } public ImageOperationTests() { this.provider = new FakeImageOperationsProvider(); - this.processor = new Mock>().Object; + this.processorImplementation = new Mock>().Object; + + Mock processorMock = new Mock(); + processorMock.Setup(p => p.CreatePixelSpecificProcessor()).Returns(this.processorImplementation); + this.processorDefinition = processorMock.Object; + this.image = new Image(new Configuration() { ImageOperationsProvider = this.provider @@ -38,61 +45,61 @@ namespace SixLabors.ImageSharp.Tests [Fact] public void MutateCallsImageOperationsProvider_Func_OriginalImage() { - this.image.Mutate(x => x.ApplyProcessor(this.processor)); + this.image.Mutate(x => x.ApplyProcessor(this.processorDefinition)); Assert.True(this.provider.HasCreated(this.image)); - Assert.Contains(this.processor, this.provider.AppliedOperations(this.image).Select(x => x.GenericProcessor)); + Assert.Contains(this.processorImplementation, this.provider.AppliedOperations(this.image).Select(x => x.GenericProcessor)); } [Fact] public void MutateCallsImageOperationsProvider_ListOfProcessors_OriginalImage() { - this.image.Mutate(this.processor); + this.image.Mutate(this.processorDefinition); Assert.True(this.provider.HasCreated(this.image)); - Assert.Contains(this.processor, this.provider.AppliedOperations(this.image).Select(x => x.GenericProcessor)); + Assert.Contains(this.processorImplementation, this.provider.AppliedOperations(this.image).Select(x => x.GenericProcessor)); } [Fact] public void CloneCallsImageOperationsProvider_Func_WithDuplicateImage() { - Image returned = this.image.Clone(x => x.ApplyProcessor(this.processor)); + Image returned = this.image.Clone(x => x.ApplyProcessor(this.processorDefinition)); Assert.True(this.provider.HasCreated(returned)); - Assert.Contains(this.processor, this.provider.AppliedOperations(returned).Select(x => x.GenericProcessor)); + Assert.Contains(this.processorImplementation, this.provider.AppliedOperations(returned).Select(x => x.GenericProcessor)); } [Fact] public void CloneCallsImageOperationsProvider_ListOfProcessors_WithDuplicateImage() { - Image returned = this.image.Clone(this.processor); + Image returned = this.image.Clone(this.processorDefinition); Assert.True(this.provider.HasCreated(returned)); - Assert.Contains(this.processor, this.provider.AppliedOperations(returned).Select(x => x.GenericProcessor)); + Assert.Contains(this.processorImplementation, this.provider.AppliedOperations(returned).Select(x => x.GenericProcessor)); } [Fact] public void CloneCallsImageOperationsProvider_Func_NotOnOrigional() { - Image returned = this.image.Clone(x => x.ApplyProcessor(this.processor)); + Image returned = this.image.Clone(x => x.ApplyProcessor(this.processorDefinition)); Assert.False(this.provider.HasCreated(this.image)); - Assert.DoesNotContain(this.processor, this.provider.AppliedOperations(this.image).Select(x => x.GenericProcessor)); + Assert.DoesNotContain(this.processorImplementation, this.provider.AppliedOperations(this.image).Select(x => x.GenericProcessor)); } [Fact] public void CloneCallsImageOperationsProvider_ListOfProcessors_NotOnOrigional() { - Image returned = this.image.Clone(this.processor); + Image returned = this.image.Clone(this.processorDefinition); Assert.False(this.provider.HasCreated(this.image)); - Assert.DoesNotContain(this.processor, this.provider.AppliedOperations(this.image).Select(x => x.GenericProcessor)); + Assert.DoesNotContain(this.processorImplementation, this.provider.AppliedOperations(this.image).Select(x => x.GenericProcessor)); } [Fact] public void ApplyProcessors_ListOfProcessors_AppliesAllProcessorsToOperation() { var operations = new FakeImageOperationsProvider.FakeImageOperations(null, false); - operations.ApplyProcessors(this.processor); - Assert.Contains(this.processor, operations.Applied.Select(x => x.GenericProcessor)); + operations.ApplyProcessors(this.processorDefinition); + Assert.Contains(this.processorImplementation, operations.Applied.Select(x => x.GenericProcessor)); } public void Dispose() => this.image.Dispose(); diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/Basic1ParameterConvolutionTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/Basic1ParameterConvolutionTests.cs index 1f939a281b..0a10d0755f 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/Basic1ParameterConvolutionTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/Basic1ParameterConvolutionTests.cs @@ -47,10 +47,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution ValidatorComparer); } - protected abstract void Apply(IImageProcessingContext ctx, int value) - where TPixel : struct, IPixel; + protected abstract void Apply(IImageProcessingContext ctx, int value); - protected abstract void Apply(IImageProcessingContext ctx, int value, Rectangle bounds) - where TPixel : struct, IPixel; + protected abstract void Apply(IImageProcessingContext ctx, int value, Rectangle bounds); } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BoxBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BoxBlurTest.cs index 923f9d6161..a7cf9360cf 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BoxBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BoxBlurTest.cs @@ -9,9 +9,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution [GroupOutput("Convolution")] public class BoxBlurTest : Basic1ParameterConvolutionTests { - protected override void Apply(IImageProcessingContext ctx, int value) => ctx.BoxBlur(value); + protected override void Apply(IImageProcessingContext ctx, int value) => ctx.BoxBlur(value); - protected override void Apply(IImageProcessingContext ctx, int value, Rectangle bounds) => + protected override void Apply(IImageProcessingContext ctx, int value, Rectangle bounds) => ctx.BoxBlur(value, bounds); } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs index 6307a1c51d..68eb8c4f63 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs @@ -13,9 +13,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution [GroupOutput("Convolution")] public class GaussianBlurTest : Basic1ParameterConvolutionTests { - protected override void Apply(IImageProcessingContext ctx, int value) => ctx.GaussianBlur(value); + protected override void Apply(IImageProcessingContext ctx, int value) => ctx.GaussianBlur(value); - protected override void Apply(IImageProcessingContext ctx, int value, Rectangle bounds) => + protected override void Apply(IImageProcessingContext ctx, int value, Rectangle bounds) => ctx.GaussianBlur(value, bounds); } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianSharpenTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianSharpenTest.cs index 29a1643b0c..4b497309ee 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianSharpenTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianSharpenTest.cs @@ -12,9 +12,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution [GroupOutput("Convolution")] public class GaussianSharpenTest : Basic1ParameterConvolutionTests { - protected override void Apply(IImageProcessingContext ctx, int value) => ctx.GaussianSharpen(value); + protected override void Apply(IImageProcessingContext ctx, int value) => ctx.GaussianSharpen(value); - protected override void Apply(IImageProcessingContext ctx, int value, Rectangle bounds) => + protected override void Apply(IImageProcessingContext ctx, int value, Rectangle bounds) => ctx.GaussianSharpen(value, bounds); } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs index 9bf9079d41..8ae298c51e 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs @@ -322,11 +322,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms } FormattableString testOutputDetails = $"{samplerName}-{destSizeInfo}"; - ctx.Apply( - img => img.DebugSave( - provider, - $"{testOutputDetails}-ORIGINAL", - appendPixelTypeToFileName: false)); + ctx.Resize((Size)newSize, sampler, false); return testOutputDetails; }, diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs index 15fab9b2bf..8d493c7d43 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs @@ -104,7 +104,7 @@ namespace SixLabors.ImageSharp.Tests /// /// Returns an instance to the test case with the necessary traits. /// - public Image GetImage(Action> operationsToApply) + public Image GetImage(Action operationsToApply) { Image img = this.GetImage(); img.Mutate(operationsToApply); diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index e77b1974d3..8de3a347ba 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -9,10 +9,13 @@ using System.Numerics; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.ParallelUtils; using SixLabors.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; +using SixLabors.Primitives; using Xunit; @@ -21,39 +24,11 @@ namespace SixLabors.ImageSharp.Tests public static class TestImageExtensions { /// - /// TODO: This should be a common processing method! The image.Opacity(val) multiplies the alpha channel! + /// TODO: Consider adding this private processor to the library /// - /// /// - public static void MakeOpaque(this IImageProcessingContext ctx) - where TPixel : struct, IPixel - { - MemoryAllocator memoryAllocator = ctx.MemoryAllocator; - - ctx.Apply( - img => - { - Configuration configuration = img.GetConfiguration(); - using (Buffer2D temp = memoryAllocator.Allocate2D(img.Width, img.Height)) - { - Span tempSpan = temp.GetSpan(); - foreach (ImageFrame frame in img.Frames) - { - Span pixelSpan = frame.GetPixelSpan(); - - PixelOperations.Instance.ToVector4(configuration, pixelSpan, tempSpan, PixelConversionModifiers.Scale); - - for (int i = 0; i < tempSpan.Length; i++) - { - ref Vector4 v = ref tempSpan[i]; - v.W = 1F; - } - - PixelOperations.Instance.FromVector4Destructive(configuration, tempSpan, pixelSpan, PixelConversionModifiers.Scale); - } - } - }); - } + public static void MakeOpaque(this IImageProcessingContext ctx) => + ctx.ApplyProcessor(new MakeOpaqueProcessor()); public static void DebugSave( this Image image, @@ -702,5 +677,37 @@ namespace SixLabors.ImageSharp.Tests return image; } + private class MakeOpaqueProcessor : IImageProcessor + { + public IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel + { + return new MakeOpaqueProcessor(); + } + } + + private class MakeOpaqueProcessor : ImageProcessor + where TPixel : struct, IPixel + { + protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + ParallelHelper.IterateRowsWithTempBuffer(sourceRectangle, configuration, + (rows, temp) => + { + Span tempSpan = temp.Span; + for (int y = rows.Min; y < rows.Max; y++) + { + var rowSpan = source.GetPixelRowSpan(y).Slice(sourceRectangle.Left, sourceRectangle.Width); + PixelOperations.Instance.ToVector4(configuration, rowSpan, tempSpan, PixelConversionModifiers.Scale); + for (int i = 0; i < tempSpan.Length; i++) + { + ref Vector4 v = ref tempSpan[i]; + v.W = 1F; + } + PixelOperations.Instance.FromVector4Destructive(configuration, tempSpan, rowSpan, PixelConversionModifiers.Scale); + } + }); + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs b/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs index 5db0c08282..b56ce05171 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs @@ -165,7 +165,7 @@ namespace SixLabors.ImageSharp.Tests /// internal static void RunValidatingProcessorTest( this TestImageProvider provider, - Action> process, + Action process, object testOutputDetails = null, ImageComparer comparer = null, bool appendPixelTypeToFileName = true, @@ -202,7 +202,7 @@ namespace SixLabors.ImageSharp.Tests internal static void RunValidatingProcessorTest( this TestImageProvider provider, - Func, FormattableString> processAndGetTestOutputDetails, + Func processAndGetTestOutputDetails, ImageComparer comparer = null, bool appendPixelTypeToFileName = true, bool appendSourceFileOrDescription = true) @@ -241,7 +241,7 @@ namespace SixLabors.ImageSharp.Tests public static void RunValidatingProcessorTestOnWrappedMemoryImage( this TestImageProvider provider, - Action> process, + Action process, object testOutputDetails = null, ImageComparer comparer = null, string useReferenceOutputFrom = null, @@ -291,11 +291,11 @@ namespace SixLabors.ImageSharp.Tests } /// - /// Same as but with an additional parameter passed to 'process' + /// Same as 'RunValidatingProcessorTest{TPixel}' but with an additional parameter passed to 'process' /// internal static void RunRectangleConstrainedValidatingProcessorTest( this TestImageProvider provider, - Action, Rectangle> process, + Action process, object testOutputDetails = null, ImageComparer comparer = null) where TPixel : struct, IPixel @@ -315,11 +315,11 @@ namespace SixLabors.ImageSharp.Tests } /// - /// Same as but without the 'CompareToReferenceOutput()' step. + /// Same as 'RunValidatingProcessorTest{TPixel}' but without the 'CompareToReferenceOutput()' step. /// internal static void RunProcessorTest( this TestImageProvider provider, - Action> process, + Action process, object testOutputDetails = null) where TPixel : struct, IPixel { From 01182219512325a36edb5eddc1ab9201cacb050f Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 17 May 2019 00:06:37 +0200 Subject: [PATCH 196/223] drop almost all usages of FileTestBase --- .../Binarization/BinaryDitherTests.cs | 19 ++++++++++--------- .../Binarization/BinaryThresholdTest.cs | 15 +++++++++++---- .../Processors/Transforms/PadTest.cs | 9 +++++++-- .../Processors/Transforms/RotateFlipTests.cs | 6 +++--- 4 files changed, 31 insertions(+), 18 deletions(-) diff --git a/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryDitherTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryDitherTests.cs index 44fdfc7039..d3507ed4c4 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryDitherTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryDitherTests.cs @@ -12,7 +12,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization { - public class BinaryDitherTests : FileTestBase + public class BinaryDitherTests { public static readonly string[] CommonTestImages = { @@ -40,14 +40,15 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization { "Stucki", KnownDiffusers.Stucki }, }; + public const PixelTypes TestPixelTypes = PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.Rgb24; private static IOrderedDither DefaultDitherer => KnownDitherers.BayerDither4x4; private static IErrorDiffuser DefaultErrorDiffuser => KnownDiffusers.Atkinson; [Theory] - [WithFileCollection(nameof(CommonTestImages), nameof(OrderedDitherers), DefaultPixelType)] - [WithTestPatternImages(nameof(OrderedDitherers), 100, 100, DefaultPixelType)] + [WithFileCollection(nameof(CommonTestImages), nameof(OrderedDitherers), PixelTypes.Rgba32)] + [WithTestPatternImages(nameof(OrderedDitherers), 100, 100, PixelTypes.Rgba32)] public void BinaryDitherFilter_WorksWithAllDitherers(TestImageProvider provider, string name, IOrderedDither ditherer) where TPixel : struct, IPixel { @@ -59,8 +60,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization } [Theory] - [WithFileCollection(nameof(CommonTestImages), nameof(ErrorDiffusers), DefaultPixelType)] - [WithTestPatternImages(nameof(ErrorDiffusers), 100, 100, DefaultPixelType)] + [WithFileCollection(nameof(CommonTestImages), nameof(ErrorDiffusers), PixelTypes.Rgba32)] + [WithTestPatternImages(nameof(ErrorDiffusers), 100, 100, PixelTypes.Rgba32)] public void DiffusionFilter_WorksWithAllErrorDiffusers(TestImageProvider provider, string name, IErrorDiffuser diffuser) where TPixel : struct, IPixel { @@ -72,7 +73,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization } [Theory] - [WithFile(TestImages.Png.Bike, CommonNonDefaultPixelTypes)] + [WithFile(TestImages.Png.Bike, TestPixelTypes)] public void BinaryDitherFilter_ShouldNotDependOnSinglePixelType(TestImageProvider provider) where TPixel : struct, IPixel { @@ -84,7 +85,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization } [Theory] - [WithFile(TestImages.Png.Bike, CommonNonDefaultPixelTypes)] + [WithFile(TestImages.Png.Bike, TestPixelTypes)] public void DiffusionFilter_ShouldNotDependOnSinglePixelType(TestImageProvider provider) where TPixel : struct, IPixel { @@ -96,7 +97,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization } [Theory] - [WithFile(TestImages.Png.CalliphoraPartial, DefaultPixelType)] + [WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Rgba32)] public void ApplyDitherFilterInBox(TestImageProvider provider) where TPixel : struct, IPixel { @@ -113,7 +114,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization } [Theory] - [WithFile(TestImages.Png.CalliphoraPartial, DefaultPixelType)] + [WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Rgba32)] public void ApplyDiffusionFilterInBox(TestImageProvider provider) where TPixel : struct, IPixel { diff --git a/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryThresholdTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryThresholdTest.cs index 988c9125ba..4ae5d60513 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryThresholdTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryThresholdTest.cs @@ -11,17 +11,24 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization { using SixLabors.ImageSharp.Processing; - public class BinaryThresholdTest : FileTestBase + public class BinaryThresholdTest { public static readonly TheoryData BinaryThresholdValues - = new TheoryData + = new TheoryData { .25F, .75F }; + + public static readonly string[] CommonTestImages = + { + TestImages.Png.CalliphoraPartial, TestImages.Png.Bike + }; + + public const PixelTypes TestPixelTypes = PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.Rgb24; [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(BinaryThresholdValues), DefaultPixelType)] + [WithFileCollection(nameof(CommonTestImages), nameof(BinaryThresholdValues), PixelTypes.Rgba32)] public void ImageShouldApplyBinaryThresholdFilter(TestImageProvider provider, float value) where TPixel : struct, IPixel { @@ -33,7 +40,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization } [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(BinaryThresholdValues), DefaultPixelType)] + [WithFileCollection(nameof(CommonTestImages), nameof(BinaryThresholdValues), PixelTypes.Rgba32)] public void ImageShouldApplyBinaryThresholdInBox(TestImageProvider provider, float value) where TPixel : struct, IPixel { diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/PadTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/PadTest.cs index 6cce62d14e..750d67c32a 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/PadTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/PadTest.cs @@ -7,10 +7,15 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms { - public class PadTest : FileTestBase + public class PadTest { + public static readonly string[] CommonTestImages = + { + TestImages.Png.CalliphoraPartial, TestImages.Png.Bike + }; + [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + [WithFileCollection(nameof(CommonTestImages), PixelTypes.Rgba32)] public void ImageShouldPad(TestImageProvider provider) where TPixel : struct, IPixel { diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateFlipTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateFlipTests.cs index d6376b1792..20e12cb7f9 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateFlipTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateFlipTests.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms { using SixLabors.ImageSharp.Processing; - public class RotateFlipTests : FileTestBase + public class RotateFlipTests { public static readonly string[] FlipFiles = { TestImages.Bmp.F }; @@ -24,8 +24,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms }; [Theory] - [WithTestPatternImages(nameof(RotateFlipValues), 100, 50, DefaultPixelType)] - [WithTestPatternImages(nameof(RotateFlipValues), 50, 100, DefaultPixelType)] + [WithTestPatternImages(nameof(RotateFlipValues), 100, 50, PixelTypes.Rgba32)] + [WithTestPatternImages(nameof(RotateFlipValues), 50, 100, PixelTypes.Rgba32)] public void RotateFlip(TestImageProvider provider, RotateMode rotateType, FlipMode flipType) where TPixel : struct, IPixel { From 25d2c35b7c5931e8c754429c8e28e3099b0aec74 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 17 May 2019 00:29:06 +0200 Subject: [PATCH 197/223] fix tests --- .../Drawing/DrawImageTests.cs | 5 ++- tests/ImageSharp.Tests/ImageOperationTests.cs | 34 +++++++++++-------- tests/ImageSharp.Tests/Issues/Issue412.cs | 3 +- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs b/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs index cb44651d6f..52821cf478 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs @@ -112,7 +112,10 @@ namespace SixLabors.ImageSharp.Tests.Drawing image.Mutate(c => c.DrawImage(brushImage, 0.5f)); image.DebugSave(provider, appendSourceFileOrDescription: false); - image.CompareToReferenceOutput(provider, appendSourceFileOrDescription: false); + image.CompareToReferenceOutput( + ImageComparer.TolerantPercentage(0.01f), + provider, + appendSourceFileOrDescription: false); } } diff --git a/tests/ImageSharp.Tests/ImageOperationTests.cs b/tests/ImageSharp.Tests/ImageOperationTests.cs index b99ad64b24..30371f0008 100644 --- a/tests/ImageSharp.Tests/ImageOperationTests.cs +++ b/tests/ImageSharp.Tests/ImageOperationTests.cs @@ -21,19 +21,13 @@ namespace SixLabors.ImageSharp.Tests { private readonly Image image; private readonly FakeImageOperationsProvider provider; - private readonly IImageProcessor processorImplementation; - private readonly IImageProcessor processorDefinition; - public Configuration Configuration { get; private set; } - public ImageOperationTests() { this.provider = new FakeImageOperationsProvider(); - this.processorImplementation = new Mock>().Object; - + Mock processorMock = new Mock(); - processorMock.Setup(p => p.CreatePixelSpecificProcessor()).Returns(this.processorImplementation); this.processorDefinition = processorMock.Object; this.image = new Image(new Configuration() @@ -48,7 +42,9 @@ namespace SixLabors.ImageSharp.Tests this.image.Mutate(x => x.ApplyProcessor(this.processorDefinition)); Assert.True(this.provider.HasCreated(this.image)); - Assert.Contains(this.processorImplementation, this.provider.AppliedOperations(this.image).Select(x => x.GenericProcessor)); + Assert.Contains( + this.processorDefinition, + this.provider.AppliedOperations(this.image).Select(x => x.NonGenericProcessor)); } [Fact] @@ -57,7 +53,9 @@ namespace SixLabors.ImageSharp.Tests this.image.Mutate(this.processorDefinition); Assert.True(this.provider.HasCreated(this.image)); - Assert.Contains(this.processorImplementation, this.provider.AppliedOperations(this.image).Select(x => x.GenericProcessor)); + Assert.Contains( + this.processorDefinition, + this.provider.AppliedOperations(this.image).Select(x => x.NonGenericProcessor)); } [Fact] @@ -66,7 +64,9 @@ namespace SixLabors.ImageSharp.Tests Image returned = this.image.Clone(x => x.ApplyProcessor(this.processorDefinition)); Assert.True(this.provider.HasCreated(returned)); - Assert.Contains(this.processorImplementation, this.provider.AppliedOperations(returned).Select(x => x.GenericProcessor)); + Assert.Contains( + this.processorDefinition, + this.provider.AppliedOperations(returned).Select(x => x.NonGenericProcessor)); } [Fact] @@ -75,7 +75,9 @@ namespace SixLabors.ImageSharp.Tests Image returned = this.image.Clone(this.processorDefinition); Assert.True(this.provider.HasCreated(returned)); - Assert.Contains(this.processorImplementation, this.provider.AppliedOperations(returned).Select(x => x.GenericProcessor)); + Assert.Contains( + this.processorDefinition, + this.provider.AppliedOperations(returned).Select(x => x.NonGenericProcessor)); } [Fact] @@ -83,7 +85,9 @@ namespace SixLabors.ImageSharp.Tests { Image returned = this.image.Clone(x => x.ApplyProcessor(this.processorDefinition)); Assert.False(this.provider.HasCreated(this.image)); - Assert.DoesNotContain(this.processorImplementation, this.provider.AppliedOperations(this.image).Select(x => x.GenericProcessor)); + Assert.DoesNotContain( + this.processorDefinition, + this.provider.AppliedOperations(this.image).Select(x => x.NonGenericProcessor)); } [Fact] @@ -91,7 +95,9 @@ namespace SixLabors.ImageSharp.Tests { Image returned = this.image.Clone(this.processorDefinition); Assert.False(this.provider.HasCreated(this.image)); - Assert.DoesNotContain(this.processorImplementation, this.provider.AppliedOperations(this.image).Select(x => x.GenericProcessor)); + Assert.DoesNotContain( + this.processorDefinition, + this.provider.AppliedOperations(this.image).Select(x => x.NonGenericProcessor)); } [Fact] @@ -99,7 +105,7 @@ namespace SixLabors.ImageSharp.Tests { var operations = new FakeImageOperationsProvider.FakeImageOperations(null, false); operations.ApplyProcessors(this.processorDefinition); - Assert.Contains(this.processorImplementation, operations.Applied.Select(x => x.GenericProcessor)); + Assert.Contains(this.processorDefinition, operations.Applied.Select(x => x.NonGenericProcessor)); } public void Dispose() => this.image.Dispose(); diff --git a/tests/ImageSharp.Tests/Issues/Issue412.cs b/tests/ImageSharp.Tests/Issues/Issue412.cs index f22d3a85cf..7b2cfd81c6 100644 --- a/tests/ImageSharp.Tests/Issues/Issue412.cs +++ b/tests/ImageSharp.Tests/Issues/Issue412.cs @@ -46,8 +46,9 @@ namespace SixLabors.ImageSharp.Tests.Issues { for (var x = 0; x < 40; x++) { + TPixel red = Color.Red.ToPixel(); - Assert.True(Color.Red.Equals(image[x, y]), $"expected {Color.Red} but found {image[x, y]} at [{x}, {y}]"); + Assert.True(red.Equals(image[x, y]), $"expected {Color.Red} but found {image[x, y]} at [{x}, {y}]"); } } } From 3589820cd69b0dd6aa96aece9ba43ad0328afdaa Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 17 May 2019 01:01:20 +0200 Subject: [PATCH 198/223] re-enable target frameworks --- src/ImageSharp.Drawing/ImageSharp.Drawing.csproj | 3 +-- src/ImageSharp/ImageSharp.csproj | 3 +-- tests/ImageSharp.Tests/ImageSharp.Tests.csproj | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj index 722ef33ff0..f9181b45da 100644 --- a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj +++ b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj @@ -11,8 +11,7 @@ $(packageversion) 0.0.1 - - netstandard2.0 + netcoreapp2.1;netstandard1.3;netstandard2.0 7.3 true diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index 1b213c26dc..832a6ce3f9 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -11,8 +11,7 @@ $(packageversion) 0.0.1 - - netstandard2.0 + netcoreapp2.1;netstandard1.3;netstandard2.0;net472 true true diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index efdf89964c..0b727f30ce 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -1,7 +1,6 @@  - - netcoreapp2.1 + netcoreapp2.1;net462;net472 True latest full From 824496f72d6279c279b9aa196eaac238eca6aa90 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sat, 18 May 2019 11:44:47 +0200 Subject: [PATCH 199/223] Add tests for quantization of 32 bit bitmap to 8 bit color image --- .../Formats/Bmp/BmpEncoderTests.cs | 44 +++++++++++++++++++ tests/Images/External | 2 +- 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs index 162d3f30b9..36bd2bbdb3 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs @@ -3,10 +3,12 @@ using System.IO; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Quantization; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using Xunit; @@ -180,6 +182,48 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp bitsPerPixel, supportTransparency: true); + [Theory] + [WithFile(Bit32Rgb, PixelTypes.Rgba32)] + public void Encode_8BitColor_WithWuQuantizer(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + var encoder = new BmpEncoder + { + BitsPerPixel = BmpBitsPerPixel.Pixel8, + Quantizer = new WuQuantizer(256) + }; + string actualOutputFile = provider.Utility.SaveTestOutputFile(image, "bmp", encoder, appendPixelTypeToFileName: false); + IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(actualOutputFile); + using (var referenceImage = Image.Load(actualOutputFile, referenceDecoder)) + { + referenceImage.CompareToReferenceOutput(ImageComparer.Exact, provider, extension: "bmp", appendPixelTypeToFileName: false); + } + } + } + + [Theory] + [WithFile(Bit32Rgb, PixelTypes.Rgba32)] + public void Encode_8BitColor_WithOctreeQuantizer(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + var encoder = new BmpEncoder + { + BitsPerPixel = BmpBitsPerPixel.Pixel8, + Quantizer = new OctreeQuantizer(256) + }; + string actualOutputFile = provider.Utility.SaveTestOutputFile(image, "bmp", encoder, appendPixelTypeToFileName: false); + IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(actualOutputFile); + using (var referenceImage = Image.Load(actualOutputFile, referenceDecoder)) + { + referenceImage.CompareToReferenceOutput(ImageComparer.Exact, provider, extension: "bmp", appendPixelTypeToFileName: false); + } + } + } + [Theory] [WithFile(TestImages.Png.GrayAlpha2BitInterlaced, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel32)] [WithFile(Bit32Rgba, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel32)] diff --git a/tests/Images/External b/tests/Images/External index c057090b44..42b3b980ed 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit c057090b4402120a83a8efe251aa5b691db9c0dc +Subproject commit 42b3b980ed07afd7b6603a5bfa6ffb91d6c8a124 From 467a91bfe0504be2f81a1f4d8d82084c052dacfc Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sat, 18 May 2019 12:07:47 +0200 Subject: [PATCH 200/223] Renamed DrawImageTest to DrawImageTests, fixed namespace --- .../Drawing/{DrawImageTest.cs => DrawImageTests.cs} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename tests/ImageSharp.Tests/Drawing/{DrawImageTest.cs => DrawImageTests.cs} (99%) diff --git a/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs b/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs similarity index 99% rename from tests/ImageSharp.Tests/Drawing/DrawImageTest.cs rename to tests/ImageSharp.Tests/Drawing/DrawImageTests.cs index 54b04390ec..0d0a821251 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs @@ -8,10 +8,10 @@ using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using SixLabors.Primitives; using Xunit; -namespace SixLabors.ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests.Drawing { [GroupOutput("Drawing")] - public class DrawImageTest : FileTestBase + public class DrawImageTests : FileTestBase { private const PixelTypes PixelTypes = Tests.PixelTypes.Rgba32; From 305b99ee8856d77bb4743510b611b4514ab3bc7d Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sat, 18 May 2019 12:49:35 +0200 Subject: [PATCH 201/223] Using tolerant comparer for the Encode_8BitColor tests --- tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs index 36bd2bbdb3..f4c3154daf 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs @@ -198,7 +198,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(actualOutputFile); using (var referenceImage = Image.Load(actualOutputFile, referenceDecoder)) { - referenceImage.CompareToReferenceOutput(ImageComparer.Exact, provider, extension: "bmp", appendPixelTypeToFileName: false); + referenceImage.CompareToReferenceOutput(ImageComparer.TolerantPercentage(0.01f), provider, extension: "bmp", appendPixelTypeToFileName: false); } } } @@ -219,7 +219,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(actualOutputFile); using (var referenceImage = Image.Load(actualOutputFile, referenceDecoder)) { - referenceImage.CompareToReferenceOutput(ImageComparer.Exact, provider, extension: "bmp", appendPixelTypeToFileName: false); + referenceImage.CompareToReferenceOutput(ImageComparer.TolerantPercentage(0.01f), provider, extension: "bmp", appendPixelTypeToFileName: false); } } } From 13412336922b1372a4bbdd1f864a07b4f9446091 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sat, 18 May 2019 19:06:44 +0200 Subject: [PATCH 202/223] Removed the Encode_8BitColor, because of the quantization differences with net472 and netcore --- .../Formats/Bmp/BmpEncoderTests.cs | 42 ------------------- 1 file changed, 42 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs index f4c3154daf..9208de694d 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs @@ -182,48 +182,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp bitsPerPixel, supportTransparency: true); - [Theory] - [WithFile(Bit32Rgb, PixelTypes.Rgba32)] - public void Encode_8BitColor_WithWuQuantizer(TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - var encoder = new BmpEncoder - { - BitsPerPixel = BmpBitsPerPixel.Pixel8, - Quantizer = new WuQuantizer(256) - }; - string actualOutputFile = provider.Utility.SaveTestOutputFile(image, "bmp", encoder, appendPixelTypeToFileName: false); - IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(actualOutputFile); - using (var referenceImage = Image.Load(actualOutputFile, referenceDecoder)) - { - referenceImage.CompareToReferenceOutput(ImageComparer.TolerantPercentage(0.01f), provider, extension: "bmp", appendPixelTypeToFileName: false); - } - } - } - - [Theory] - [WithFile(Bit32Rgb, PixelTypes.Rgba32)] - public void Encode_8BitColor_WithOctreeQuantizer(TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - var encoder = new BmpEncoder - { - BitsPerPixel = BmpBitsPerPixel.Pixel8, - Quantizer = new OctreeQuantizer(256) - }; - string actualOutputFile = provider.Utility.SaveTestOutputFile(image, "bmp", encoder, appendPixelTypeToFileName: false); - IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(actualOutputFile); - using (var referenceImage = Image.Load(actualOutputFile, referenceDecoder)) - { - referenceImage.CompareToReferenceOutput(ImageComparer.TolerantPercentage(0.01f), provider, extension: "bmp", appendPixelTypeToFileName: false); - } - } - } - [Theory] [WithFile(TestImages.Png.GrayAlpha2BitInterlaced, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel32)] [WithFile(Bit32Rgba, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel32)] From 71a27f27b9f40f29de5ffbe0848de3256a528cdc Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 19 May 2019 18:34:07 +0200 Subject: [PATCH 203/223] Re-enabled Encode_8BitColor tests, if its 64 bit process --- .../Formats/Bmp/BmpEncoderTests.cs | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs index 9208de694d..178e652ae7 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs @@ -182,6 +182,58 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp bitsPerPixel, supportTransparency: true); + [Theory] + [WithFile(Bit32Rgb, PixelTypes.Rgba32)] + public void Encode_8BitColor_WithWuQuantizer(TestImageProvider provider) + where TPixel : struct, IPixel + { + if (!TestEnvironment.Is64BitProcess) + { + return; + } + + using (Image image = provider.GetImage()) + { + var encoder = new BmpEncoder + { + BitsPerPixel = BmpBitsPerPixel.Pixel8, + Quantizer = new WuQuantizer(256) + }; + string actualOutputFile = provider.Utility.SaveTestOutputFile(image, "bmp", encoder, appendPixelTypeToFileName: false); + IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(actualOutputFile); + using (var referenceImage = Image.Load(actualOutputFile, referenceDecoder)) + { + referenceImage.CompareToReferenceOutput(ImageComparer.TolerantPercentage(0.01f), provider, extension: "bmp", appendPixelTypeToFileName: false); + } + } + } + + [Theory] + [WithFile(Bit32Rgb, PixelTypes.Rgba32)] + public void Encode_8BitColor_WithOctreeQuantizer(TestImageProvider provider) + where TPixel : struct, IPixel + { + if (!TestEnvironment.Is64BitProcess) + { + return; + } + + using (Image image = provider.GetImage()) + { + var encoder = new BmpEncoder + { + BitsPerPixel = BmpBitsPerPixel.Pixel8, + Quantizer = new OctreeQuantizer(256) + }; + string actualOutputFile = provider.Utility.SaveTestOutputFile(image, "bmp", encoder, appendPixelTypeToFileName: false); + IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(actualOutputFile); + using (var referenceImage = Image.Load(actualOutputFile, referenceDecoder)) + { + referenceImage.CompareToReferenceOutput(ImageComparer.TolerantPercentage(0.01f), provider, extension: "bmp", appendPixelTypeToFileName: false); + } + } + } + [Theory] [WithFile(TestImages.Png.GrayAlpha2BitInterlaced, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel32)] [WithFile(Bit32Rgba, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel32)] From b51d127bd27b665fb9beea9dede3a9ec44e1e96e Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 19 May 2019 20:56:20 +0200 Subject: [PATCH 204/223] Using MemoryMarshal.AsBytes in Write8BitGray to get the bytes of a row --- src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs | 23 +++++++------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index e9600f640d..9fbd0b5adb 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -4,6 +4,7 @@ using System; using System.Buffers; using System.IO; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Common.Helpers; @@ -389,23 +390,15 @@ namespace SixLabors.ImageSharp.Formats.Bmp stream.Write(colorPalette); - using (IMemoryOwner rowSpanBuffer = this.memoryAllocator.AllocateManagedByteBuffer(image.Width, AllocationOptions.Clean)) + for (int y = image.Height - 1; y >= 0; y--) { - Span outputPixelRow = rowSpanBuffer.GetSpan(); - for (int y = image.Height - 1; y >= 0; y--) - { - Span inputPixelRow = image.GetPixelRowSpan(y); - PixelOperations.Instance.ToGray8Bytes( - this.configuration, - inputPixelRow, - outputPixelRow, - image.Width); - stream.Write(outputPixelRow); + ReadOnlySpan inputPixelRow = image.GetPixelRowSpan(y); + ReadOnlySpan outputPixelRow = MemoryMarshal.AsBytes(inputPixelRow); + stream.Write(outputPixelRow); - for (int i = 0; i < this.padding; i++) - { - stream.WriteByte(0); - } + for (int i = 0; i < this.padding; i++) + { + stream.WriteByte(0); } } } From d03eaa24f9af30ed2468f3adfbebe590a9859ff5 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 19 May 2019 21:28:39 +0200 Subject: [PATCH 205/223] Fix merge mistake: Using DrawImageTests from current master branch --- .../Drawing/DrawImageTests.cs | 228 +++++++++--------- 1 file changed, 116 insertions(+), 112 deletions(-) diff --git a/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs b/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs index 0d0a821251..b2d499c7fa 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs @@ -2,58 +2,37 @@ // Licensed under the Apache License, Version 2.0. using System; + +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using SixLabors.Primitives; +using SixLabors.Shapes; + using Xunit; namespace SixLabors.ImageSharp.Tests.Drawing { [GroupOutput("Drawing")] - public class DrawImageTests : FileTestBase + public class DrawImageTests { - private const PixelTypes PixelTypes = Tests.PixelTypes.Rgba32; - - public static readonly string[] TestFiles = { - TestImages.Jpeg.Baseline.Calliphora, - TestImages.Bmp.Car, - TestImages.Png.Splash, - TestImages.Gif.Rings - }; - - [Theory] - [WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Normal)] - [WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Multiply)] - [WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Add)] - [WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Subtract)] - [WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Screen)] - [WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Darken)] - [WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Lighten)] - [WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Overlay)] - [WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.HardLight)] - public void ImageShouldApplyDrawImage(TestImageProvider provider, PixelColorBlendingMode mode) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - using (var blend = Image.Load(TestFile.Create(TestImages.Bmp.Car).Bytes)) + public static readonly TheoryData BlendingModes = new TheoryData { - blend.Mutate(x => x.Resize(image.Width / 2, image.Height / 2)); - image.Mutate(x => x.DrawImage(blend, new Point(image.Width / 4, image.Height / 4), mode, .75f)); - image.DebugSave(provider, new { mode }); - } - } + PixelColorBlendingMode.Normal, + PixelColorBlendingMode.Multiply, + PixelColorBlendingMode.Add, + PixelColorBlendingMode.Subtract, + PixelColorBlendingMode.Screen, + PixelColorBlendingMode.Darken, + PixelColorBlendingMode.Lighten, + PixelColorBlendingMode.Overlay, + PixelColorBlendingMode.HardLight, + }; [Theory] - [WithFile(TestImages.Png.Rainbow, PixelTypes, PixelColorBlendingMode.Normal)] - [WithFile(TestImages.Png.Rainbow, PixelTypes, PixelColorBlendingMode.Multiply)] - [WithFile(TestImages.Png.Rainbow, PixelTypes, PixelColorBlendingMode.Add)] - [WithFile(TestImages.Png.Rainbow, PixelTypes, PixelColorBlendingMode.Subtract)] - [WithFile(TestImages.Png.Rainbow, PixelTypes, PixelColorBlendingMode.Screen)] - [WithFile(TestImages.Png.Rainbow, PixelTypes, PixelColorBlendingMode.Darken)] - [WithFile(TestImages.Png.Rainbow, PixelTypes, PixelColorBlendingMode.Lighten)] - [WithFile(TestImages.Png.Rainbow, PixelTypes, PixelColorBlendingMode.Overlay)] - [WithFile(TestImages.Png.Rainbow, PixelTypes, PixelColorBlendingMode.HardLight)] + [WithFile(TestImages.Png.Rainbow, nameof(BlendingModes), PixelTypes.Rgba32)] public void ImageBlendingMatchesSvgSpecExamples(TestImageProvider provider, PixelColorBlendingMode mode) where TPixel : struct, IPixel { @@ -61,96 +40,139 @@ namespace SixLabors.ImageSharp.Tests.Drawing using (var source = Image.Load(TestFile.Create(TestImages.Png.Ducky).Bytes)) { background.Mutate(x => x.DrawImage(source, mode, 1F)); - VerifyImage(provider, mode, background); + background.DebugSave( + provider, + new { mode = mode }, + appendPixelTypeToFileName: false, + appendSourceFileOrDescription: false); + + var comparer = ImageComparer.TolerantPercentage(0.01F); + background.CompareToReferenceOutput(comparer, + provider, + new { mode = mode }, + appendPixelTypeToFileName: false, + appendSourceFileOrDescription: false); } } [Theory] - [WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Normal)] - public void ImageShouldDrawTransformedImage(TestImageProvider provider, PixelColorBlendingMode mode) + [WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Rgba32, TestImages.Png.Splash, PixelColorBlendingMode.Normal, 1f)] + [WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Bgr24, TestImages.Png.Bike, PixelColorBlendingMode.Normal, 1f)] + [WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Rgba32, TestImages.Png.Splash, PixelColorBlendingMode.Normal, 0.75f)] + [WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Rgba32, TestImages.Png.Splash, PixelColorBlendingMode.Normal, 0.25f)] + + [WithTestPatternImages(400, 400, PixelTypes.Rgba32, TestImages.Png.Splash, PixelColorBlendingMode.Multiply, 0.5f)] + [WithTestPatternImages(400, 400, PixelTypes.Rgba32, TestImages.Png.Splash, PixelColorBlendingMode.Add, 0.5f)] + [WithTestPatternImages(400, 400, PixelTypes.Rgba32, TestImages.Png.Splash, PixelColorBlendingMode.Subtract, 0.5f)] + + [WithFile(TestImages.Png.Rgb48Bpp, PixelTypes.Rgba64, TestImages.Png.Splash, PixelColorBlendingMode.Normal, 1f)] + [WithFile(TestImages.Png.Rgb48Bpp, PixelTypes.Rgba64, TestImages.Png.Splash, PixelColorBlendingMode.Normal, 0.25f)] + public void WorksWithDifferentConfigurations( + TestImageProvider provider, + string brushImage, + PixelColorBlendingMode mode, + float opacity) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) - using (var blend = Image.Load(TestFile.Create(TestImages.Bmp.Car).Bytes)) + using (var blend = Image.Load(TestFile.Create(brushImage).Bytes)) { - AffineTransformBuilder builder = new AffineTransformBuilder() - .AppendRotationDegrees(45F) - .AppendScale(new SizeF(.25F, .25F)) - .AppendTranslation(new PointF(10, 10)); + Size size = new Size(image.Width * 3 / 4, image.Height * 3 / 4); + Point position = new Point(image.Width / 8, image.Height / 8); + blend.Mutate(x => x.Resize(size.Width, size.Height, KnownResamplers.Bicubic)); + image.Mutate(x => x.DrawImage(blend, position, mode, opacity)); + FormattableString testInfo = $"{System.IO.Path.GetFileNameWithoutExtension(brushImage)}-{mode}-{opacity}"; - // Apply a background color so we can see the translation. - blend.Mutate(x => x.Transform(builder)); - blend.Mutate(x => x.BackgroundColor(Color.HotPink)); + PngEncoder encoder = new PngEncoder(); - // Lets center the matrix so we can tell whether any cut-off issues we may have belong to the drawing processor - var position = new Point((image.Width - blend.Width) / 2, (image.Height - blend.Height) / 2); - image.Mutate(x => x.DrawImage(blend, position, mode, .75F)); - image.DebugSave(provider, new[] { "Transformed" }); + if (provider.PixelType == PixelTypes.Rgba64) + { + encoder.BitDepth = PngBitDepth.Bit16; + } + + image.DebugSave(provider, testInfo, encoder: encoder); + image.CompareToReferenceOutput(ImageComparer.TolerantPercentage(0.01f), + provider, + testInfo); } } [Theory] - [WithSolidFilledImages(100, 100, 255, 255, 255, PixelTypes.Rgba32)] - public void ImageShouldHandleNegativeLocation(TestImageProvider provider) + [WithTestPatternImages(200, 200, PixelTypes.Rgba32 | PixelTypes.Bgra32)] + public void DrawImageOfDifferentPixelType(TestImageProvider provider) + where TPixel : struct, IPixel { - using (Image background = provider.GetImage()) - using (var overlay = new Image(50, 50)) - { - overlay.Mutate(x => x.Fill(Rgba32.Black)); - - const int xy = -25; - Rgba32 backgroundPixel = background[0, 0]; - Rgba32 overlayPixel = overlay[Math.Abs(xy) + 1, Math.Abs(xy) + 1]; + byte[] brushData = TestFile.Create(TestImages.Png.Ducky).Bytes; - background.Mutate(x => x.DrawImage(overlay, new Point(xy, xy), PixelColorBlendingMode.Normal, 1F)); - - Assert.Equal(Rgba32.White, backgroundPixel); - Assert.Equal(overlayPixel, background[0, 0]); + using (Image image = provider.GetImage()) + using (Image brushImage = provider.PixelType == PixelTypes.Rgba32 + ? (Image)Image.Load(brushData) + : Image.Load(brushData)) + { + image.Mutate(c => c.DrawImage(brushImage, 0.5f)); - background.DebugSave(provider, testOutputDetails: "Negative"); + image.DebugSave(provider, appendSourceFileOrDescription: false); + image.CompareToReferenceOutput( + ImageComparer.TolerantPercentage(0.01f), + provider, + appendSourceFileOrDescription: false); } } [Theory] - [WithSolidFilledImages(100, 100, 255, 255, 255, PixelTypes.Rgba32)] - public void ImageShouldHandlePositiveLocation(TestImageProvider provider) + [WithSolidFilledImages(100, 100, "White", PixelTypes.Rgba32, 0, 0)] + [WithSolidFilledImages(100, 100, "White", PixelTypes.Rgba32, 25, 25)] + [WithSolidFilledImages(100, 100, "White", PixelTypes.Rgba32, 75, 50)] + [WithSolidFilledImages(100, 100, "White", PixelTypes.Rgba32, -25, -30)] + public void WorksWithDifferentLocations(TestImageProvider provider, int x, int y) { using (Image background = provider.GetImage()) using (var overlay = new Image(50, 50)) { - overlay.Mutate(x => x.Fill(Rgba32.Black)); - - const int xy = 25; - Rgba32 backgroundPixel = background[xy - 1, xy - 1]; - Rgba32 overlayPixel = overlay[0, 0]; + overlay.Mutate(c => c.Fill(Rgba32.Black)); - background.Mutate(x => x.DrawImage(overlay, new Point(xy, xy), PixelColorBlendingMode.Normal, 1F)); + background.Mutate(c => c.DrawImage(overlay, new Point(x, y), PixelColorBlendingMode.Normal, 1F)); - Assert.Equal(Rgba32.White, backgroundPixel); - Assert.Equal(overlayPixel, background[xy, xy]); + background.DebugSave( + provider, + testOutputDetails: $"{x}_{y}", + appendPixelTypeToFileName: false, + appendSourceFileOrDescription: false); - background.DebugSave(provider, testOutputDetails: "Positive"); + background.CompareToReferenceOutput( + provider, + testOutputDetails: $"{x}_{y}", + appendPixelTypeToFileName: false, + appendSourceFileOrDescription: false); } } + [Theory] - [WithSolidFilledImages(100, 100, 255, 255, 255, PixelTypes.Rgba32)] - public void ImageShouldHandlePositiveLocationTruncatedOverlay(TestImageProvider provider) + [WithFile(TestImages.Png.Splash, PixelTypes.Rgba32)] + public void DrawTransformed(TestImageProvider provider) + where TPixel : struct, IPixel { - using (Image background = provider.GetImage()) - using (var overlay = new Image(50, 50)) + using (Image image = provider.GetImage()) + using (var blend = Image.Load(TestFile.Create(TestImages.Bmp.Car).Bytes)) { - overlay.Mutate(x => x.Fill(Rgba32.Black)); - - const int xy = 75; - Rgba32 backgroundPixel = background[xy - 1, xy - 1]; - Rgba32 overlayPixel = overlay[0, 0]; + AffineTransformBuilder builder = new AffineTransformBuilder() + .AppendRotationDegrees(45F) + .AppendScale(new SizeF(.25F, .25F)) + .AppendTranslation(new PointF(10, 10)); - background.Mutate(x => x.DrawImage(overlay, new Point(xy, xy), PixelColorBlendingMode.Normal, 1F)); + // Apply a background color so we can see the translation. + blend.Mutate(x => x.Transform(builder)); + blend.Mutate(x => x.BackgroundColor(Color.HotPink)); - Assert.Equal(Rgba32.White, backgroundPixel); - Assert.Equal(overlayPixel, background[xy, xy]); + // Lets center the matrix so we can tell whether any cut-off issues we may have belong to the drawing processor + var position = new Point((image.Width - blend.Width) / 2, (image.Height - blend.Height) / 2); + image.Mutate(x => x.DrawImage(blend, position, .75F)); - background.DebugSave(provider, testOutputDetails: "PositiveTruncated"); + image.DebugSave(provider, appendSourceFileOrDescription: false, appendPixelTypeToFileName: false); + image.CompareToReferenceOutput(ImageComparer.TolerantPercentage(0.002f), + provider, + appendSourceFileOrDescription: false, + appendPixelTypeToFileName: false); } } @@ -175,24 +197,6 @@ namespace SixLabors.ImageSharp.Tests.Drawing } } - private static void VerifyImage( - TestImageProvider provider, - PixelColorBlendingMode mode, - Image img) - where TPixel : struct, IPixel - { - img.DebugSave( - provider, - new { mode }, - appendPixelTypeToFileName: false, - appendSourceFileOrDescription: false); - - var comparer = ImageComparer.TolerantPercentage(0.01F, 3); - img.CompareFirstFrameToReferenceOutput(comparer, - provider, - new { mode }, - appendPixelTypeToFileName: false, - appendSourceFileOrDescription: false); - } + } } \ No newline at end of file From 34781bff3eeded66e99acd91d99f40bb079ef9a8 Mon Sep 17 00:00:00 2001 From: Anton Firsov Date: Thu, 23 May 2019 13:00:48 +0200 Subject: [PATCH 206/223] API cleanup (related to #907) (#911) * temporarily disable target frameworks * drop DelegateProcessor * drop IImageProcessingContext * drop NamedColors * drop ColorBuilder * drop the *Base postfix for clean class hierarchies * re-enable target frameworks * use MathF in gradient brushes * Move PngFilterMethod to the correct namespace. --- .../Processing/EllipticGradientBrush.cs | 11 +- ...{GradientBrushBase.cs => GradientBrush.cs} | 10 +- .../Processing/LinearGradientBrush.cs | 8 +- .../Processing/RadialGradientBrush.cs | 8 +- src/ImageSharp/Color/Color.cs | 48 +- .../Conversion/ColorSpaceConverter.CieXyz.cs | 2 +- .../Conversion/ColorSpaceConverter.cs | 2 +- .../Conversion/ColorSpaceConverterOptions.cs | 2 +- .../Converters/CieXyzToLinearRgbConverter.cs | 4 +- .../LinearRgbAndCieXyzConverterBase.cs | 2 +- .../Converters/LinearRgbToCieXyzConverter.cs | 4 +- .../RGBPrimariesChromaticityCoordinates.cs | 2 +- .../WorkingSpaces/GammaWorkingSpace.cs | 2 +- .../WorkingSpaces/LWorkingSpace.cs | 2 +- .../WorkingSpaces/Rec2020WorkingSpace.cs | 2 +- .../WorkingSpaces/Rec709WorkingSpace.cs | 2 +- ...WorkingSpaceBase.cs => RgbWorkingSpace.cs} | 10 +- .../WorkingSpaces/SRgbWorkingSpace.cs | 2 +- src/ImageSharp/ColorSpaces/LinearRgb.cs | 10 +- src/ImageSharp/ColorSpaces/Rgb.cs | 10 +- .../ColorSpaces/RgbWorkingSpaces.cs | 38 +- .../Formats/{ => Png}/PngFilterMethod.cs | 2 +- .../PixelFormats/ColorBuilder{TPixel}.cs | 104 --- .../PixelFormats/NamedColors{TPixel}.cs | 761 ------------------ .../PixelImplementations/Rgba32.cs | 55 +- .../PixelImplementations/RgbaVector.cs | 2 +- ...ext.cs => DefaultImageProcessorContext.cs} | 14 +- .../Extensions/BinaryDiffuseExtensions.cs | 8 +- .../Extensions/BinaryDitherExtensions.cs | 8 +- .../Extensions/ProcessingExtensions.cs | 37 +- .../IImageProcessingContextFactory.cs | 2 +- .../IImageProcessingContext{TPixel}.cs | 46 -- .../IInternalImageProcessingContext.cs | 22 + .../Processors/DelegateProcessor.cs | 43 - .../Processors/Dithering/AtkinsonDiffuser.cs | 2 +- .../Processors/Dithering/BurksDiffuser.cs | 2 +- ...{ErrorDiffuserBase.cs => ErrorDiffuser.cs} | 6 +- .../Dithering/FloydSteinbergDiffuser.cs | 2 +- .../Dithering/JarvisJudiceNinkeDiffuser.cs | 2 +- .../Processors/Dithering/Sierra2Diffuser.cs | 2 +- .../Processors/Dithering/Sierra3Diffuser.cs | 2 +- .../Dithering/SierraLiteDiffuser.cs | 2 +- .../Dithering/StevensonArceDiffuser.cs | 2 +- .../Processors/Dithering/StuckiDiffuser.cs | 2 +- .../AffineTransformProcessor{TPixel}.cs | 2 +- .../Transforms/CropProcessor{TPixel}.cs | 2 +- .../ProjectiveTransformProcessor{TPixel}.cs | 2 +- .../Resize/ResizeProcessor{TPixel}.cs | 2 +- ...ProcessorBase.cs => TransformProcessor.cs} | 2 +- tests/ImageSharp.Benchmarks/Samplers/Glow.cs | 2 +- .../BaseImageOperationsExtensionTest.cs | 2 +- .../ApproximateColorspaceComparer.cs | 6 +- .../FakeImageOperationsProvider.cs | 21 +- .../Image/ImageProcessingContextTests.cs | 60 -- .../Image/ImageTests.WrapMemory.cs | 8 +- .../PixelFormats/ColorBuilderTests.cs | 40 - .../PixelFormats/ColorDefinitionTests.cs | 46 -- .../Processing/DelegateTest.cs | 25 - .../BasicTestPatternProvider.cs | 10 +- .../ImageProviders/TestPatternProvider.cs | 31 +- 60 files changed, 223 insertions(+), 1347 deletions(-) rename src/ImageSharp.Drawing/Processing/{GradientBrushBase.cs => GradientBrush.cs} (96%) rename src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/{RgbWorkingSpaceBase.cs => RgbWorkingSpace.cs} (91%) rename src/ImageSharp/Formats/{ => Png}/PngFilterMethod.cs (97%) delete mode 100644 src/ImageSharp/PixelFormats/ColorBuilder{TPixel}.cs delete mode 100644 src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs rename src/ImageSharp/Processing/{DefaultInternalImageProcessorContext.cs => DefaultImageProcessorContext.cs} (85%) delete mode 100644 src/ImageSharp/Processing/IImageProcessingContext{TPixel}.cs create mode 100644 src/ImageSharp/Processing/IInternalImageProcessingContext.cs delete mode 100644 src/ImageSharp/Processing/Processors/DelegateProcessor.cs rename src/ImageSharp/Processing/Processors/Dithering/{ErrorDiffuserBase.cs => ErrorDiffuser.cs} (96%) rename src/ImageSharp/Processing/Processors/Transforms/{TransformProcessorBase.cs => TransformProcessor.cs} (89%) delete mode 100644 tests/ImageSharp.Tests/Image/ImageProcessingContextTests.cs delete mode 100644 tests/ImageSharp.Tests/PixelFormats/ColorBuilderTests.cs delete mode 100644 tests/ImageSharp.Tests/PixelFormats/ColorDefinitionTests.cs delete mode 100644 tests/ImageSharp.Tests/Processing/DelegateTest.cs diff --git a/src/ImageSharp.Drawing/Processing/EllipticGradientBrush.cs b/src/ImageSharp.Drawing/Processing/EllipticGradientBrush.cs index 4a8ce44dbb..91da332a16 100644 --- a/src/ImageSharp.Drawing/Processing/EllipticGradientBrush.cs +++ b/src/ImageSharp.Drawing/Processing/EllipticGradientBrush.cs @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Processing /// a point on the longest extension of the ellipse and /// the ratio between longest and shortest extension. /// - public sealed class EllipticGradientBrush : GradientBrushBase + public sealed class EllipticGradientBrush : GradientBrush { private readonly PointF center; @@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Processing private readonly float axisRatio; - /// + /// /// The center of the elliptical gradient and 0 for the color stops. /// The end point of the reference axis of the ellipse. /// @@ -60,7 +60,7 @@ namespace SixLabors.ImageSharp.Processing this.RepetitionMode); /// - private sealed class RadialGradientBrushApplicator : GradientBrushApplicatorBase + private sealed class RadialGradientBrushApplicator : GradientBrushApplicator where TPixel : struct, IPixel { private readonly PointF center; @@ -149,8 +149,7 @@ namespace SixLabors.ImageSharp.Processing { var vA = a - junction; var vB = b - junction; - return (float)(Math.Atan2(vB.Y, vB.X) - - Math.Atan2(vA.Y, vA.X)); + return MathF.Atan2(vB.Y, vB.X) - MathF.Atan2(vA.Y, vA.X); } private float DistanceBetween( @@ -162,7 +161,7 @@ namespace SixLabors.ImageSharp.Processing float dY = p1.Y - p2.Y; float dYsquared = dY * dY; - return (float)Math.Sqrt(dXsquared + dYsquared); + return MathF.Sqrt(dXsquared + dYsquared); } } } diff --git a/src/ImageSharp.Drawing/Processing/GradientBrushBase.cs b/src/ImageSharp.Drawing/Processing/GradientBrush.cs similarity index 96% rename from src/ImageSharp.Drawing/Processing/GradientBrushBase.cs rename to src/ImageSharp.Drawing/Processing/GradientBrush.cs index 39dbcb61a5..9826748c46 100644 --- a/src/ImageSharp.Drawing/Processing/GradientBrushBase.cs +++ b/src/ImageSharp.Drawing/Processing/GradientBrush.cs @@ -13,12 +13,12 @@ namespace SixLabors.ImageSharp.Processing /// /// Base class for Gradient brushes /// - public abstract class GradientBrushBase : IBrush + public abstract class GradientBrush : IBrush { /// /// Defines how the colors are repeated beyond the interval [0..1] /// The gradient colors. - protected GradientBrushBase( + protected GradientBrush( GradientRepetitionMode repetitionMode, params ColorStop[] colorStops) { @@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.Processing /// /// Base class for gradient brush applicators /// - internal abstract class GradientBrushApplicatorBase : BrushApplicator + internal abstract class GradientBrushApplicator : BrushApplicator where TPixel : struct, IPixel { private static readonly TPixel Transparent = Color.Transparent.ToPixel(); @@ -56,13 +56,13 @@ namespace SixLabors.ImageSharp.Processing private readonly GradientRepetitionMode repetitionMode; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The target. /// The options. /// An array of color stops sorted by their position. /// Defines if and how the gradient should be repeated. - protected GradientBrushApplicatorBase( + protected GradientBrushApplicator( ImageFrame target, GraphicsOptions options, ColorStop[] colorStops, diff --git a/src/ImageSharp.Drawing/Processing/LinearGradientBrush.cs b/src/ImageSharp.Drawing/Processing/LinearGradientBrush.cs index 6eeadee5d6..bb99eeb26a 100644 --- a/src/ImageSharp.Drawing/Processing/LinearGradientBrush.cs +++ b/src/ImageSharp.Drawing/Processing/LinearGradientBrush.cs @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Processing /// Supported right now: /// - a set of colors in relative distances to each other. /// - public sealed class LinearGradientBrush : GradientBrushBase + public sealed class LinearGradientBrush : GradientBrush { private readonly PointF p1; @@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The linear gradient brush applicator. /// - private sealed class LinearGradientBrushApplicator : GradientBrushApplicatorBase + private sealed class LinearGradientBrushApplicator : GradientBrushApplicator where TPixel : struct, IPixel { private readonly PointF start; @@ -121,7 +121,7 @@ namespace SixLabors.ImageSharp.Processing // some helpers: this.alongsSquared = (this.alongX * this.alongX) + (this.alongY * this.alongY); - this.length = (float)Math.Sqrt(this.alongsSquared); + this.length = MathF.Sqrt(this.alongsSquared); } protected override float PositionOnGradient(float x, float y) @@ -145,7 +145,7 @@ namespace SixLabors.ImageSharp.Processing float y4 = y + (k * this.alongX); // get distance from (x4,y4) to start - float distance = (float)Math.Sqrt(Math.Pow(x4 - this.start.X, 2) + Math.Pow(y4 - this.start.Y, 2)); + float distance = MathF.Sqrt(MathF.Pow(x4 - this.start.X, 2) + MathF.Pow(y4 - this.start.Y, 2)); // get and return ratio float ratio = distance / this.length; diff --git a/src/ImageSharp.Drawing/Processing/RadialGradientBrush.cs b/src/ImageSharp.Drawing/Processing/RadialGradientBrush.cs index aac0f2e7bd..f4d2dd81f4 100644 --- a/src/ImageSharp.Drawing/Processing/RadialGradientBrush.cs +++ b/src/ImageSharp.Drawing/Processing/RadialGradientBrush.cs @@ -11,13 +11,13 @@ namespace SixLabors.ImageSharp.Processing /// /// A Circular Gradient Brush, defined by center point and radius. /// - public sealed class RadialGradientBrush : GradientBrushBase + public sealed class RadialGradientBrush : GradientBrush { private readonly PointF center; private readonly float radius; - /// + /// /// The center of the circular gradient and 0 for the color stops. /// The radius of the circular gradient and 1 for the color stops. /// Defines how the colors in the gradient are repeated. @@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp.Processing this.RepetitionMode); /// - private sealed class RadialGradientBrushApplicator : GradientBrushApplicatorBase + private sealed class RadialGradientBrushApplicator : GradientBrushApplicator where TPixel : struct, IPixel { private readonly PointF center; @@ -90,7 +90,7 @@ namespace SixLabors.ImageSharp.Processing /// the position on the color gradient. protected override float PositionOnGradient(float x, float y) { - float distance = (float)Math.Sqrt(Math.Pow(this.center.X - x, 2) + Math.Pow(this.center.Y - y, 2)); + float distance = MathF.Sqrt(MathF.Pow(this.center.X - x, 2) + MathF.Pow(this.center.Y - y, 2)); return distance / this.radius; } diff --git a/src/ImageSharp/Color/Color.cs b/src/ImageSharp/Color/Color.cs index 0d94ac18c8..2547e6d873 100644 --- a/src/ImageSharp/Color/Color.cs +++ b/src/ImageSharp/Color/Color.cs @@ -104,18 +104,11 @@ namespace SixLabors.ImageSharp /// in rgb, rgba, rrggbb, or rrggbbaa format to match web syntax. /// /// Returns a that represents the color defined by the provided RGBA hex string. + [MethodImpl(InliningOptions.ShortMethod)] public static Color FromHex(string hex) { - Guard.NotNullOrWhiteSpace(hex, nameof(hex)); - - hex = ToRgbaHex(hex); - - if (hex is null || !uint.TryParse(hex, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out uint packedValue)) - { - throw new ArgumentException("Hexadecimal string is not in the correct format.", nameof(hex)); - } + Rgba32 rgba = Rgba32.FromHex(hex); - var rgba = new Rgba32(BinaryPrimitives.ReverseEndianness(packedValue)); return new Color(rgba); } @@ -189,42 +182,5 @@ namespace SixLabors.ImageSharp ReadOnlySpan rgba64Span = MemoryMarshal.Cast(source); PixelOperations.Instance.FromRgba64(Configuration.Default, rgba64Span, destination); } - - /// - /// Converts the specified hex value to an rrggbbaa hex value. - /// - /// The hex value to convert. - /// - /// A rrggbbaa hex value. - /// - private static string ToRgbaHex(string hex) - { - if (hex[0] == '#') - { - hex = hex.Substring(1); - } - - if (hex.Length == 8) - { - return hex; - } - - if (hex.Length == 6) - { - return hex + "FF"; - } - - if (hex.Length < 3 || hex.Length > 4) - { - return null; - } - - char r = hex[0]; - char g = hex[1]; - char b = hex[2]; - char a = hex.Length == 3 ? 'F' : hex[3]; - - return new string(new[] { r, r, g, g, b, b, a, a }); - } } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs index 2cc8437bba..21d576fb41 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs @@ -465,7 +465,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// /// The source working space /// The - private LinearRgbToCieXyzConverter GetLinearRgbToCieXyzConverter(RgbWorkingSpaceBase workingSpace) + private LinearRgbToCieXyzConverter GetLinearRgbToCieXyzConverter(RgbWorkingSpace workingSpace) { if (this.linearRgbToCieXyzConverter?.SourceWorkingSpace.Equals(workingSpace) == true) { diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs index fe6a57f7ac..bcbd64c77a 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs @@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion private readonly CieXyz targetLuvWhitePoint; private readonly CieXyz targetLabWhitePoint; private readonly CieXyz targetHunterLabWhitePoint; - private readonly RgbWorkingSpaceBase targetRgbWorkingSpace; + private readonly RgbWorkingSpace targetRgbWorkingSpace; private readonly IChromaticAdaptation chromaticAdaptation; private readonly bool performChromaticAdaptation; private readonly CieXyzAndLmsConverter cieXyzAndLmsConverter; diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverterOptions.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverterOptions.cs index fcd031e263..65fe799949 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverterOptions.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverterOptions.cs @@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// Gets or sets the target working space used *when creating* RGB colors. (RGB colors on the input already contain the working space information) /// Defaults to: . /// - public RgbWorkingSpaceBase TargetRgbWorkingSpace { get; set; } = Rgb.DefaultWorkingSpace; + public RgbWorkingSpace TargetRgbWorkingSpace { get; set; } = Rgb.DefaultWorkingSpace; /// /// Gets or sets the chromatic adaptation method used. When null, no adaptation will be performed. diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToLinearRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToLinearRgbConverter.cs index f166e4c14a..6497e3060c 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToLinearRgbConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToLinearRgbConverter.cs @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation /// Initializes a new instance of the class. /// /// The target working space. - public CieXyzToLinearRgbConverter(RgbWorkingSpaceBase workingSpace) + public CieXyzToLinearRgbConverter(RgbWorkingSpace workingSpace) { this.TargetWorkingSpace = workingSpace; @@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation /// /// Gets the target working space. /// - public RgbWorkingSpaceBase TargetWorkingSpace { get; } + public RgbWorkingSpace TargetWorkingSpace { get; } /// /// Performs the conversion from the input to an instance of type. diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbAndCieXyzConverterBase.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbAndCieXyzConverterBase.cs index a93773262c..bdf451cd3c 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbAndCieXyzConverterBase.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbAndCieXyzConverterBase.cs @@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation /// /// The Rgb working space. /// The based on the chromaticity and working space. - public static Matrix4x4 GetRgbToCieXyzMatrix(RgbWorkingSpaceBase workingSpace) + public static Matrix4x4 GetRgbToCieXyzMatrix(RgbWorkingSpace workingSpace) { DebugGuard.NotNull(workingSpace, nameof(workingSpace)); RgbPrimariesChromaticityCoordinates chromaticity = workingSpace.ChromaticityCoordinates; diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToCieXyzConverter.cs index 1030ac9819..21a96071af 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToCieXyzConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToCieXyzConverter.cs @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation /// Initializes a new instance of the class. /// /// The target working space. - public LinearRgbToCieXyzConverter(RgbWorkingSpaceBase workingSpace) + public LinearRgbToCieXyzConverter(RgbWorkingSpace workingSpace) { this.SourceWorkingSpace = workingSpace; this.conversionMatrix = GetRgbToCieXyzMatrix(workingSpace); @@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation /// /// Gets the source working space /// - public RgbWorkingSpaceBase SourceWorkingSpace { get; } + public RgbWorkingSpace SourceWorkingSpace { get; } /// /// Performs the conversion from the input to an instance of type. diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/RGBPrimariesChromaticityCoordinates.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/RGBPrimariesChromaticityCoordinates.cs index 4c69133e0f..e28f3f1a4a 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/RGBPrimariesChromaticityCoordinates.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/RGBPrimariesChromaticityCoordinates.cs @@ -7,7 +7,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation { /// /// Represents the chromaticity coordinates of RGB primaries. - /// One of the specifiers of . + /// One of the specifiers of . /// public readonly struct RgbPrimariesChromaticityCoordinates : IEquatable { diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs index c45dea3179..6caca54cdc 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs @@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation /// /// The gamma working space. /// - public sealed class GammaWorkingSpace : RgbWorkingSpaceBase + public sealed class GammaWorkingSpace : RgbWorkingSpace { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/LWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/LWorkingSpace.cs index 16617ea242..a2eb42ad0b 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/LWorkingSpace.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/LWorkingSpace.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation /// /// L* working space. /// - public sealed class LWorkingSpace : RgbWorkingSpaceBase + public sealed class LWorkingSpace : RgbWorkingSpace { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec2020WorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec2020WorkingSpace.cs index 9ba1ff8811..a794b3dda7 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec2020WorkingSpace.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec2020WorkingSpace.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation /// /// Rec. 2020 (ITU-R Recommendation BT.2020F) working space. /// - public sealed class Rec2020WorkingSpace : RgbWorkingSpaceBase + public sealed class Rec2020WorkingSpace : RgbWorkingSpace { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec709WorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec709WorkingSpace.cs index 88623e958d..ffa9699bc5 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec709WorkingSpace.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec709WorkingSpace.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation /// /// Rec. 709 (ITU-R Recommendation BT.709) working space. /// - public sealed class Rec709WorkingSpace : RgbWorkingSpaceBase + public sealed class Rec709WorkingSpace : RgbWorkingSpace { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/RgbWorkingSpaceBase.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/RgbWorkingSpace.cs similarity index 91% rename from src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/RgbWorkingSpaceBase.cs rename to src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/RgbWorkingSpace.cs index 6051f52865..a97ae22b18 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/RgbWorkingSpaceBase.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/RgbWorkingSpace.cs @@ -6,16 +6,16 @@ using System; namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation { /// - /// Base class for all implementations of . + /// Base class for all implementations of . /// - public abstract class RgbWorkingSpaceBase + public abstract class RgbWorkingSpace { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The reference white point. /// The chromaticity of the rgb primaries. - protected RgbWorkingSpaceBase(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) + protected RgbWorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) { this.WhitePoint = referenceWhite; this.ChromaticityCoordinates = chromaticityCoordinates; @@ -66,7 +66,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation return true; } - if (obj is RgbWorkingSpaceBase other) + if (obj is RgbWorkingSpace other) { return this.WhitePoint.Equals(other.WhitePoint) && this.ChromaticityCoordinates.Equals(other.ChromaticityCoordinates); diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/SRgbWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/SRgbWorkingSpace.cs index b44db06817..c3d850251a 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/SRgbWorkingSpace.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/SRgbWorkingSpace.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation /// /// The sRgb working space. /// - public sealed class SRgbWorkingSpace : RgbWorkingSpaceBase + public sealed class SRgbWorkingSpace : RgbWorkingSpace { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/ColorSpaces/LinearRgb.cs b/src/ImageSharp/ColorSpaces/LinearRgb.cs index 46b2275968..7ef50e9c41 100644 --- a/src/ImageSharp/ColorSpaces/LinearRgb.cs +++ b/src/ImageSharp/ColorSpaces/LinearRgb.cs @@ -9,7 +9,7 @@ using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; namespace SixLabors.ImageSharp.ColorSpaces { /// - /// Represents an linear Rgb color with specified working space + /// Represents an linear Rgb color with specified working space /// public readonly struct LinearRgb : IEquatable { @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// /// The default LinearRgb working space. /// - public static readonly RgbWorkingSpaceBase DefaultWorkingSpace = RgbWorkingSpaces.SRgb; + public static readonly RgbWorkingSpace DefaultWorkingSpace = RgbWorkingSpaces.SRgb; /// /// Gets the red component. @@ -42,7 +42,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// /// Gets the LinearRgb color space /// - public readonly RgbWorkingSpaceBase WorkingSpace; + public readonly RgbWorkingSpace WorkingSpace; /// /// Initializes a new instance of the struct. @@ -64,7 +64,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// The blue component ranging between 0 and 1. /// The rgb working space. [MethodImpl(InliningOptions.ShortMethod)] - public LinearRgb(float r, float g, float b, RgbWorkingSpaceBase workingSpace) + public LinearRgb(float r, float g, float b, RgbWorkingSpace workingSpace) : this(new Vector3(r, g, b), workingSpace) { } @@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// The vector representing the r, g, b components. /// The LinearRgb working space. [MethodImpl(InliningOptions.ShortMethod)] - public LinearRgb(Vector3 vector, RgbWorkingSpaceBase workingSpace) + public LinearRgb(Vector3 vector, RgbWorkingSpace workingSpace) { // Clamp to 0-1 range. vector = Vector3.Clamp(vector, Min, Max); diff --git a/src/ImageSharp/ColorSpaces/Rgb.cs b/src/ImageSharp/ColorSpaces/Rgb.cs index 5cb89a2931..bb71deba3b 100644 --- a/src/ImageSharp/ColorSpaces/Rgb.cs +++ b/src/ImageSharp/ColorSpaces/Rgb.cs @@ -10,14 +10,14 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.ColorSpaces { /// - /// Represents an RGB color with specified working space. + /// Represents an RGB color with specified working space. /// public readonly struct Rgb : IEquatable { /// /// The default rgb working space. /// - public static readonly RgbWorkingSpaceBase DefaultWorkingSpace = RgbWorkingSpaces.SRgb; + public static readonly RgbWorkingSpace DefaultWorkingSpace = RgbWorkingSpaces.SRgb; private static readonly Vector3 Min = Vector3.Zero; private static readonly Vector3 Max = Vector3.One; @@ -43,7 +43,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// /// Gets the Rgb color space /// - public readonly RgbWorkingSpaceBase WorkingSpace; + public readonly RgbWorkingSpace WorkingSpace; /// /// Initializes a new instance of the struct. @@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// The blue component ranging between 0 and 1. /// The rgb working space. [MethodImpl(InliningOptions.ShortMethod)] - public Rgb(float r, float g, float b, RgbWorkingSpaceBase workingSpace) + public Rgb(float r, float g, float b, RgbWorkingSpace workingSpace) : this(new Vector3(r, g, b), workingSpace) { } @@ -86,7 +86,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// The vector representing the r, g, b components. /// The rgb working space. [MethodImpl(InliningOptions.ShortMethod)] - public Rgb(Vector3 vector, RgbWorkingSpaceBase workingSpace) + public Rgb(Vector3 vector, RgbWorkingSpace workingSpace) { vector = Vector3.Clamp(vector, Min, Max); this.R = vector.X; diff --git a/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs b/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs index 7ddcae7cea..3f40fa4f58 100644 --- a/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs +++ b/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs @@ -19,97 +19,97 @@ namespace SixLabors.ImageSharp.ColorSpaces /// Uses proper companding function, according to: /// /// - public static readonly RgbWorkingSpaceBase SRgb = new SRgbWorkingSpace(Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.3000F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); + public static readonly RgbWorkingSpace SRgb = new SRgbWorkingSpace(Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.3000F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); /// /// Simplified sRgb working space (uses gamma companding instead of ). /// See also . /// - public static readonly RgbWorkingSpaceBase SRgbSimplified = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.3000F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); + public static readonly RgbWorkingSpace SRgbSimplified = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.3000F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); /// /// Rec. 709 (ITU-R Recommendation BT.709) working space. /// - public static readonly RgbWorkingSpaceBase Rec709 = new Rec709WorkingSpace(Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.64F, 0.33F), new CieXyChromaticityCoordinates(0.30F, 0.60F), new CieXyChromaticityCoordinates(0.15F, 0.06F))); + public static readonly RgbWorkingSpace Rec709 = new Rec709WorkingSpace(Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.64F, 0.33F), new CieXyChromaticityCoordinates(0.30F, 0.60F), new CieXyChromaticityCoordinates(0.15F, 0.06F))); /// /// Rec. 2020 (ITU-R Recommendation BT.2020F) working space. /// - public static readonly RgbWorkingSpaceBase Rec2020 = new Rec2020WorkingSpace(Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.708F, 0.292F), new CieXyChromaticityCoordinates(0.170F, 0.797F), new CieXyChromaticityCoordinates(0.131F, 0.046F))); + public static readonly RgbWorkingSpace Rec2020 = new Rec2020WorkingSpace(Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.708F, 0.292F), new CieXyChromaticityCoordinates(0.170F, 0.797F), new CieXyChromaticityCoordinates(0.131F, 0.046F))); /// /// ECI Rgb v2 working space. /// - public static readonly RgbWorkingSpaceBase ECIRgbv2 = new LWorkingSpace(Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6700F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1400F, 0.0800F))); + public static readonly RgbWorkingSpace ECIRgbv2 = new LWorkingSpace(Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6700F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1400F, 0.0800F))); /// /// Adobe Rgb (1998) working space. /// - public static readonly RgbWorkingSpaceBase AdobeRgb1998 = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); + public static readonly RgbWorkingSpace AdobeRgb1998 = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); /// /// Apple sRgb working space. /// - public static readonly RgbWorkingSpaceBase ApplesRgb = new GammaWorkingSpace(1.8F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6250F, 0.3400F), new CieXyChromaticityCoordinates(0.2800F, 0.5950F), new CieXyChromaticityCoordinates(0.1550F, 0.0700F))); + public static readonly RgbWorkingSpace ApplesRgb = new GammaWorkingSpace(1.8F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6250F, 0.3400F), new CieXyChromaticityCoordinates(0.2800F, 0.5950F), new CieXyChromaticityCoordinates(0.1550F, 0.0700F))); /// /// Best Rgb working space. /// - public static readonly RgbWorkingSpaceBase BestRgb = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347F, 0.2653F), new CieXyChromaticityCoordinates(0.2150F, 0.7750F), new CieXyChromaticityCoordinates(0.1300F, 0.0350F))); + public static readonly RgbWorkingSpace BestRgb = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347F, 0.2653F), new CieXyChromaticityCoordinates(0.2150F, 0.7750F), new CieXyChromaticityCoordinates(0.1300F, 0.0350F))); /// /// Beta Rgb working space. /// - public static readonly RgbWorkingSpaceBase BetaRgb = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6888F, 0.3112F), new CieXyChromaticityCoordinates(0.1986F, 0.7551F), new CieXyChromaticityCoordinates(0.1265F, 0.0352F))); + public static readonly RgbWorkingSpace BetaRgb = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6888F, 0.3112F), new CieXyChromaticityCoordinates(0.1986F, 0.7551F), new CieXyChromaticityCoordinates(0.1265F, 0.0352F))); /// /// Bruce Rgb working space. /// - public static readonly RgbWorkingSpaceBase BruceRgb = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2800F, 0.6500F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); + public static readonly RgbWorkingSpace BruceRgb = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2800F, 0.6500F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); /// /// CIE Rgb working space. /// - public static readonly RgbWorkingSpaceBase CIERgb = new GammaWorkingSpace(2.2F, Illuminants.E, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7350F, 0.2650F), new CieXyChromaticityCoordinates(0.2740F, 0.7170F), new CieXyChromaticityCoordinates(0.1670F, 0.0090F))); + public static readonly RgbWorkingSpace CIERgb = new GammaWorkingSpace(2.2F, Illuminants.E, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7350F, 0.2650F), new CieXyChromaticityCoordinates(0.2740F, 0.7170F), new CieXyChromaticityCoordinates(0.1670F, 0.0090F))); /// /// ColorMatch Rgb working space. /// - public static readonly RgbWorkingSpaceBase ColorMatchRgb = new GammaWorkingSpace(1.8F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6300F, 0.3400F), new CieXyChromaticityCoordinates(0.2950F, 0.6050F), new CieXyChromaticityCoordinates(0.1500F, 0.0750F))); + public static readonly RgbWorkingSpace ColorMatchRgb = new GammaWorkingSpace(1.8F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6300F, 0.3400F), new CieXyChromaticityCoordinates(0.2950F, 0.6050F), new CieXyChromaticityCoordinates(0.1500F, 0.0750F))); /// /// Don Rgb 4 working space. /// - public static readonly RgbWorkingSpaceBase DonRgb4 = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6960F, 0.3000F), new CieXyChromaticityCoordinates(0.2150F, 0.7650F), new CieXyChromaticityCoordinates(0.1300F, 0.0350F))); + public static readonly RgbWorkingSpace DonRgb4 = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6960F, 0.3000F), new CieXyChromaticityCoordinates(0.2150F, 0.7650F), new CieXyChromaticityCoordinates(0.1300F, 0.0350F))); /// /// Ekta Space PS5 working space. /// - public static readonly RgbWorkingSpaceBase EktaSpacePS5 = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6950F, 0.3050F), new CieXyChromaticityCoordinates(0.2600F, 0.7000F), new CieXyChromaticityCoordinates(0.1100F, 0.0050F))); + public static readonly RgbWorkingSpace EktaSpacePS5 = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6950F, 0.3050F), new CieXyChromaticityCoordinates(0.2600F, 0.7000F), new CieXyChromaticityCoordinates(0.1100F, 0.0050F))); /// /// NTSC Rgb working space. /// - public static readonly RgbWorkingSpaceBase NTSCRgb = new GammaWorkingSpace(2.2F, Illuminants.C, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6700F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1400F, 0.0800F))); + public static readonly RgbWorkingSpace NTSCRgb = new GammaWorkingSpace(2.2F, Illuminants.C, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6700F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1400F, 0.0800F))); /// /// PAL/SECAM Rgb working space. /// - public static readonly RgbWorkingSpaceBase PALSECAMRgb = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2900F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); + public static readonly RgbWorkingSpace PALSECAMRgb = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2900F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); /// /// ProPhoto Rgb working space. /// - public static readonly RgbWorkingSpaceBase ProPhotoRgb = new GammaWorkingSpace(1.8F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347F, 0.2653F), new CieXyChromaticityCoordinates(0.1596F, 0.8404F), new CieXyChromaticityCoordinates(0.0366F, 0.0001F))); + public static readonly RgbWorkingSpace ProPhotoRgb = new GammaWorkingSpace(1.8F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347F, 0.2653F), new CieXyChromaticityCoordinates(0.1596F, 0.8404F), new CieXyChromaticityCoordinates(0.0366F, 0.0001F))); /// /// SMPTE-C Rgb working space. /// - public static readonly RgbWorkingSpaceBase SMPTECRgb = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6300F, 0.3400F), new CieXyChromaticityCoordinates(0.3100F, 0.5950F), new CieXyChromaticityCoordinates(0.1550F, 0.0700F))); + public static readonly RgbWorkingSpace SMPTECRgb = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6300F, 0.3400F), new CieXyChromaticityCoordinates(0.3100F, 0.5950F), new CieXyChromaticityCoordinates(0.1550F, 0.0700F))); /// /// Wide Gamut Rgb working space. /// - public static readonly RgbWorkingSpaceBase WideGamutRgb = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7350F, 0.2650F), new CieXyChromaticityCoordinates(0.1150F, 0.8260F), new CieXyChromaticityCoordinates(0.1570F, 0.0180F))); + public static readonly RgbWorkingSpace WideGamutRgb = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7350F, 0.2650F), new CieXyChromaticityCoordinates(0.1150F, 0.8260F), new CieXyChromaticityCoordinates(0.1570F, 0.0180F))); } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/PngFilterMethod.cs b/src/ImageSharp/Formats/Png/PngFilterMethod.cs similarity index 97% rename from src/ImageSharp/Formats/PngFilterMethod.cs rename to src/ImageSharp/Formats/Png/PngFilterMethod.cs index 73c4056257..e16c1234f6 100644 --- a/src/ImageSharp/Formats/PngFilterMethod.cs +++ b/src/ImageSharp/Formats/Png/PngFilterMethod.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Png { /// /// Provides enumeration of available PNG filter methods. diff --git a/src/ImageSharp/PixelFormats/ColorBuilder{TPixel}.cs b/src/ImageSharp/PixelFormats/ColorBuilder{TPixel}.cs deleted file mode 100644 index c1fb13e4da..0000000000 --- a/src/ImageSharp/PixelFormats/ColorBuilder{TPixel}.cs +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Buffers.Binary; -using System.Globalization; - -namespace SixLabors.ImageSharp.PixelFormats -{ - /// - /// A set of named colors mapped to the provided Color space. - /// - /// The type of the color. - public static class ColorBuilder - where TPixel : struct, IPixel - { - /// - /// Creates a new representation from the string representing a color. - /// - /// - /// The hexadecimal representation of the combined color components arranged - /// in rgb, rgba, rrggbb, or rrggbbaa format to match web syntax. - /// - /// Returns a that represents the color defined by the provided RGBA hex string. - public static TPixel FromHex(string hex) - { - Guard.NotNullOrWhiteSpace(hex, nameof(hex)); - - hex = ToRgbaHex(hex); - - if (hex is null || !uint.TryParse(hex, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out uint packedValue)) - { - throw new ArgumentException("Hexadecimal string is not in the correct format.", nameof(hex)); - } - - TPixel result = default; - var rgba = new Rgba32(BinaryPrimitives.ReverseEndianness(packedValue)); - - result.FromRgba32(rgba); - return result; - } - - /// - /// Creates a new representation from standard RGB bytes with 100% opacity. - /// - /// The red intensity. - /// The green intensity. - /// The blue intensity. - /// Returns a that represents the color defined by the provided RGB values with 100% opacity. - public static TPixel FromRgb(byte red, byte green, byte blue) => FromRgba(red, green, blue, 255); - - /// - /// Creates a new representation from standard RGBA bytes. - /// - /// The red intensity. - /// The green intensity. - /// The blue intensity. - /// The alpha intensity. - /// Returns a that represents the color defined by the provided RGBA values. - public static TPixel FromRgba(byte red, byte green, byte blue, byte alpha) - { - TPixel color = default; - color.FromRgba32(new Rgba32(red, green, blue, alpha)); - return color; - } - - /// - /// Converts the specified hex value to an rrggbbaa hex value. - /// - /// The hex value to convert. - /// - /// A rrggbbaa hex value. - /// - private static string ToRgbaHex(string hex) - { - if (hex[0] == '#') - { - hex = hex.Substring(1); - } - - if (hex.Length == 8) - { - return hex; - } - - if (hex.Length == 6) - { - return hex + "FF"; - } - - if (hex.Length < 3 || hex.Length > 4) - { - return null; - } - - char r = hex[0]; - char g = hex[1]; - char b = hex[2]; - char a = hex.Length == 3 ? 'F' : hex[3]; - - return new string(new[] { r, r, g, g, b, b, a, a }); - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs b/src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs deleted file mode 100644 index 2138e2e2f7..0000000000 --- a/src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs +++ /dev/null @@ -1,761 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.PixelFormats -{ - /// - /// A set of named colors mapped to the provided color space. - /// - /// The type of the color. - public static class NamedColors - where TPixel : struct, IPixel - { - /// - /// Thread-safe backing field for the constant palettes. - /// - private static readonly Lazy WebSafePaletteLazy = new Lazy(GetWebSafePalette, true); - private static readonly Lazy WernerPaletteLazy = new Lazy(GetWernerPalette, true); - - /// - /// Represents a matching the W3C definition that has an hex value of #F0F8FF. - /// - public static readonly TPixel AliceBlue = ColorBuilder.FromRgba(240, 248, 255, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FAEBD7. - /// - public static readonly TPixel AntiqueWhite = ColorBuilder.FromRgba(250, 235, 215, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #00FFFF. - /// - public static readonly TPixel Aqua = ColorBuilder.FromRgba(0, 255, 255, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #7FFFD4. - /// - public static readonly TPixel Aquamarine = ColorBuilder.FromRgba(127, 255, 212, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #F0FFFF. - /// - public static readonly TPixel Azure = ColorBuilder.FromRgba(240, 255, 255, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #F5F5DC. - /// - public static readonly TPixel Beige = ColorBuilder.FromRgba(245, 245, 220, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFE4C4. - /// - public static readonly TPixel Bisque = ColorBuilder.FromRgba(255, 228, 196, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #000000. - /// - public static readonly TPixel Black = ColorBuilder.FromRgba(0, 0, 0, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFEBCD. - /// - public static readonly TPixel BlanchedAlmond = ColorBuilder.FromRgba(255, 235, 205, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #0000FF. - /// - public static readonly TPixel Blue = ColorBuilder.FromRgba(0, 0, 255, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #8A2BE2. - /// - public static readonly TPixel BlueViolet = ColorBuilder.FromRgba(138, 43, 226, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #A52A2A. - /// - public static readonly TPixel Brown = ColorBuilder.FromRgba(165, 42, 42, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #DEB887. - /// - public static readonly TPixel BurlyWood = ColorBuilder.FromRgba(222, 184, 135, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #5F9EA0. - /// - public static readonly TPixel CadetBlue = ColorBuilder.FromRgba(95, 158, 160, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #7FFF00. - /// - public static readonly TPixel Chartreuse = ColorBuilder.FromRgba(127, 255, 0, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #D2691E. - /// - public static readonly TPixel Chocolate = ColorBuilder.FromRgba(210, 105, 30, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FF7F50. - /// - public static readonly TPixel Coral = ColorBuilder.FromRgba(255, 127, 80, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #6495ED. - /// - public static readonly TPixel CornflowerBlue = ColorBuilder.FromRgba(100, 149, 237, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFF8DC. - /// - public static readonly TPixel Cornsilk = ColorBuilder.FromRgba(255, 248, 220, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #DC143C. - /// - public static readonly TPixel Crimson = ColorBuilder.FromRgba(220, 20, 60, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #00FFFF. - /// - public static readonly TPixel Cyan = ColorBuilder.FromRgba(0, 255, 255, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #00008B. - /// - public static readonly TPixel DarkBlue = ColorBuilder.FromRgba(0, 0, 139, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #008B8B. - /// - public static readonly TPixel DarkCyan = ColorBuilder.FromRgba(0, 139, 139, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #B8860B. - /// - public static readonly TPixel DarkGoldenrod = ColorBuilder.FromRgba(184, 134, 11, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #A9A9A9. - /// - public static readonly TPixel DarkGray = ColorBuilder.FromRgba(169, 169, 169, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #006400. - /// - public static readonly TPixel DarkGreen = ColorBuilder.FromRgba(0, 100, 0, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #BDB76B. - /// - public static readonly TPixel DarkKhaki = ColorBuilder.FromRgba(189, 183, 107, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #8B008B. - /// - public static readonly TPixel DarkMagenta = ColorBuilder.FromRgba(139, 0, 139, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #556B2F. - /// - public static readonly TPixel DarkOliveGreen = ColorBuilder.FromRgba(85, 107, 47, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FF8C00. - /// - public static readonly TPixel DarkOrange = ColorBuilder.FromRgba(255, 140, 0, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #9932CC. - /// - public static readonly TPixel DarkOrchid = ColorBuilder.FromRgba(153, 50, 204, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #8B0000. - /// - public static readonly TPixel DarkRed = ColorBuilder.FromRgba(139, 0, 0, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #E9967A. - /// - public static readonly TPixel DarkSalmon = ColorBuilder.FromRgba(233, 150, 122, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #8FBC8B. - /// - public static readonly TPixel DarkSeaGreen = ColorBuilder.FromRgba(143, 188, 139, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #483D8B. - /// - public static readonly TPixel DarkSlateBlue = ColorBuilder.FromRgba(72, 61, 139, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #2F4F4F. - /// - public static readonly TPixel DarkSlateGray = ColorBuilder.FromRgba(47, 79, 79, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #00CED1. - /// - public static readonly TPixel DarkTurquoise = ColorBuilder.FromRgba(0, 206, 209, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #9400D3. - /// - public static readonly TPixel DarkViolet = ColorBuilder.FromRgba(148, 0, 211, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FF1493. - /// - public static readonly TPixel DeepPink = ColorBuilder.FromRgba(255, 20, 147, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #00BFFF. - /// - public static readonly TPixel DeepSkyBlue = ColorBuilder.FromRgba(0, 191, 255, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #696969. - /// - public static readonly TPixel DimGray = ColorBuilder.FromRgba(105, 105, 105, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #1E90FF. - /// - public static readonly TPixel DodgerBlue = ColorBuilder.FromRgba(30, 144, 255, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #B22222. - /// - public static readonly TPixel Firebrick = ColorBuilder.FromRgba(178, 34, 34, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFFAF0. - /// - public static readonly TPixel FloralWhite = ColorBuilder.FromRgba(255, 250, 240, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #228B22. - /// - public static readonly TPixel ForestGreen = ColorBuilder.FromRgba(34, 139, 34, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FF00FF. - /// - public static readonly TPixel Fuchsia = ColorBuilder.FromRgba(255, 0, 255, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #DCDCDC. - /// - public static readonly TPixel Gainsboro = ColorBuilder.FromRgba(220, 220, 220, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #F8F8FF. - /// - public static readonly TPixel GhostWhite = ColorBuilder.FromRgba(248, 248, 255, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFD700. - /// - public static readonly TPixel Gold = ColorBuilder.FromRgba(255, 215, 0, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #DAA520. - /// - public static readonly TPixel Goldenrod = ColorBuilder.FromRgba(218, 165, 32, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #808080. - /// - public static readonly TPixel Gray = ColorBuilder.FromRgba(128, 128, 128, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #008000. - /// - public static readonly TPixel Green = ColorBuilder.FromRgba(0, 128, 0, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #ADFF2F. - /// - public static readonly TPixel GreenYellow = ColorBuilder.FromRgba(173, 255, 47, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #F0FFF0. - /// - public static readonly TPixel Honeydew = ColorBuilder.FromRgba(240, 255, 240, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FF69B4. - /// - public static readonly TPixel HotPink = ColorBuilder.FromRgba(255, 105, 180, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #CD5C5C. - /// - public static readonly TPixel IndianRed = ColorBuilder.FromRgba(205, 92, 92, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #4B0082. - /// - public static readonly TPixel Indigo = ColorBuilder.FromRgba(75, 0, 130, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFFFF0. - /// - public static readonly TPixel Ivory = ColorBuilder.FromRgba(255, 255, 240, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #F0E68C. - /// - public static readonly TPixel Khaki = ColorBuilder.FromRgba(240, 230, 140, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #E6E6FA. - /// - public static readonly TPixel Lavender = ColorBuilder.FromRgba(230, 230, 250, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFF0F5. - /// - public static readonly TPixel LavenderBlush = ColorBuilder.FromRgba(255, 240, 245, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #7CFC00. - /// - public static readonly TPixel LawnGreen = ColorBuilder.FromRgba(124, 252, 0, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFFACD. - /// - public static readonly TPixel LemonChiffon = ColorBuilder.FromRgba(255, 250, 205, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #ADD8E6. - /// - public static readonly TPixel LightBlue = ColorBuilder.FromRgba(173, 216, 230, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #F08080. - /// - public static readonly TPixel LightCoral = ColorBuilder.FromRgba(240, 128, 128, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #E0FFFF. - /// - public static readonly TPixel LightCyan = ColorBuilder.FromRgba(224, 255, 255, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FAFAD2. - /// - public static readonly TPixel LightGoldenrodYellow = ColorBuilder.FromRgba(250, 250, 210, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #D3D3D3. - /// - public static readonly TPixel LightGray = ColorBuilder.FromRgba(211, 211, 211, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #90EE90. - /// - public static readonly TPixel LightGreen = ColorBuilder.FromRgba(144, 238, 144, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFB6C1. - /// - public static readonly TPixel LightPink = ColorBuilder.FromRgba(255, 182, 193, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFA07A. - /// - public static readonly TPixel LightSalmon = ColorBuilder.FromRgba(255, 160, 122, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #20B2AA. - /// - public static readonly TPixel LightSeaGreen = ColorBuilder.FromRgba(32, 178, 170, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #87CEFA. - /// - public static readonly TPixel LightSkyBlue = ColorBuilder.FromRgba(135, 206, 250, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #778899. - /// - public static readonly TPixel LightSlateGray = ColorBuilder.FromRgba(119, 136, 153, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #B0C4DE. - /// - public static readonly TPixel LightSteelBlue = ColorBuilder.FromRgba(176, 196, 222, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFFFE0. - /// - public static readonly TPixel LightYellow = ColorBuilder.FromRgba(255, 255, 224, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #00FF00. - /// - public static readonly TPixel Lime = ColorBuilder.FromRgba(0, 255, 0, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #32CD32. - /// - public static readonly TPixel LimeGreen = ColorBuilder.FromRgba(50, 205, 50, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FAF0E6. - /// - public static readonly TPixel Linen = ColorBuilder.FromRgba(250, 240, 230, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FF00FF. - /// - public static readonly TPixel Magenta = ColorBuilder.FromRgba(255, 0, 255, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #800000. - /// - public static readonly TPixel Maroon = ColorBuilder.FromRgba(128, 0, 0, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #66CDAA. - /// - public static readonly TPixel MediumAquamarine = ColorBuilder.FromRgba(102, 205, 170, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #0000CD. - /// - public static readonly TPixel MediumBlue = ColorBuilder.FromRgba(0, 0, 205, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #BA55D3. - /// - public static readonly TPixel MediumOrchid = ColorBuilder.FromRgba(186, 85, 211, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #9370DB. - /// - public static readonly TPixel MediumPurple = ColorBuilder.FromRgba(147, 112, 219, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #3CB371. - /// - public static readonly TPixel MediumSeaGreen = ColorBuilder.FromRgba(60, 179, 113, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #7B68EE. - /// - public static readonly TPixel MediumSlateBlue = ColorBuilder.FromRgba(123, 104, 238, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #00FA9A. - /// - public static readonly TPixel MediumSpringGreen = ColorBuilder.FromRgba(0, 250, 154, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #48D1CC. - /// - public static readonly TPixel MediumTurquoise = ColorBuilder.FromRgba(72, 209, 204, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #C71585. - /// - public static readonly TPixel MediumVioletRed = ColorBuilder.FromRgba(199, 21, 133, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #191970. - /// - public static readonly TPixel MidnightBlue = ColorBuilder.FromRgba(25, 25, 112, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #F5FFFA. - /// - public static readonly TPixel MintCream = ColorBuilder.FromRgba(245, 255, 250, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFE4E1. - /// - public static readonly TPixel MistyRose = ColorBuilder.FromRgba(255, 228, 225, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFE4B5. - /// - public static readonly TPixel Moccasin = ColorBuilder.FromRgba(255, 228, 181, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFDEAD. - /// - public static readonly TPixel NavajoWhite = ColorBuilder.FromRgba(255, 222, 173, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #000080. - /// - public static readonly TPixel Navy = ColorBuilder.FromRgba(0, 0, 128, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FDF5E6. - /// - public static readonly TPixel OldLace = ColorBuilder.FromRgba(253, 245, 230, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #808000. - /// - public static readonly TPixel Olive = ColorBuilder.FromRgba(128, 128, 0, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #6B8E23. - /// - public static readonly TPixel OliveDrab = ColorBuilder.FromRgba(107, 142, 35, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFA500. - /// - public static readonly TPixel Orange = ColorBuilder.FromRgba(255, 165, 0, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FF4500. - /// - public static readonly TPixel OrangeRed = ColorBuilder.FromRgba(255, 69, 0, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #DA70D6. - /// - public static readonly TPixel Orchid = ColorBuilder.FromRgba(218, 112, 214, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #EEE8AA. - /// - public static readonly TPixel PaleGoldenrod = ColorBuilder.FromRgba(238, 232, 170, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #98FB98. - /// - public static readonly TPixel PaleGreen = ColorBuilder.FromRgba(152, 251, 152, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #AFEEEE. - /// - public static readonly TPixel PaleTurquoise = ColorBuilder.FromRgba(175, 238, 238, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #DB7093. - /// - public static readonly TPixel PaleVioletRed = ColorBuilder.FromRgba(219, 112, 147, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFEFD5. - /// - public static readonly TPixel PapayaWhip = ColorBuilder.FromRgba(255, 239, 213, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFDAB9. - /// - public static readonly TPixel PeachPuff = ColorBuilder.FromRgba(255, 218, 185, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #CD853F. - /// - public static readonly TPixel Peru = ColorBuilder.FromRgba(205, 133, 63, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFC0CB. - /// - public static readonly TPixel Pink = ColorBuilder.FromRgba(255, 192, 203, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #DDA0DD. - /// - public static readonly TPixel Plum = ColorBuilder.FromRgba(221, 160, 221, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #B0E0E6. - /// - public static readonly TPixel PowderBlue = ColorBuilder.FromRgba(176, 224, 230, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #800080. - /// - public static readonly TPixel Purple = ColorBuilder.FromRgba(128, 0, 128, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #663399. - /// - public static readonly TPixel RebeccaPurple = ColorBuilder.FromRgba(102, 51, 153, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FF0000. - /// - public static readonly TPixel Red = ColorBuilder.FromRgba(255, 0, 0, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #BC8F8F. - /// - public static readonly TPixel RosyBrown = ColorBuilder.FromRgba(188, 143, 143, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #4169E1. - /// - public static readonly TPixel RoyalBlue = ColorBuilder.FromRgba(65, 105, 225, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #8B4513. - /// - public static readonly TPixel SaddleBrown = ColorBuilder.FromRgba(139, 69, 19, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FA8072. - /// - public static readonly TPixel Salmon = ColorBuilder.FromRgba(250, 128, 114, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #F4A460. - /// - public static readonly TPixel SandyBrown = ColorBuilder.FromRgba(244, 164, 96, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #2E8B57. - /// - public static readonly TPixel SeaGreen = ColorBuilder.FromRgba(46, 139, 87, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFF5EE. - /// - public static readonly TPixel SeaShell = ColorBuilder.FromRgba(255, 245, 238, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #A0522D. - /// - public static readonly TPixel Sienna = ColorBuilder.FromRgba(160, 82, 45, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #C0C0C0. - /// - public static readonly TPixel Silver = ColorBuilder.FromRgba(192, 192, 192, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #87CEEB. - /// - public static readonly TPixel SkyBlue = ColorBuilder.FromRgba(135, 206, 235, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #6A5ACD. - /// - public static readonly TPixel SlateBlue = ColorBuilder.FromRgba(106, 90, 205, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #708090. - /// - public static readonly TPixel SlateGray = ColorBuilder.FromRgba(112, 128, 144, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFFAFA. - /// - public static readonly TPixel Snow = ColorBuilder.FromRgba(255, 250, 250, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #00FF7F. - /// - public static readonly TPixel SpringGreen = ColorBuilder.FromRgba(0, 255, 127, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #4682B4. - /// - public static readonly TPixel SteelBlue = ColorBuilder.FromRgba(70, 130, 180, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #D2B48C. - /// - public static readonly TPixel Tan = ColorBuilder.FromRgba(210, 180, 140, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #008080. - /// - public static readonly TPixel Teal = ColorBuilder.FromRgba(0, 128, 128, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #D8BFD8. - /// - public static readonly TPixel Thistle = ColorBuilder.FromRgba(216, 191, 216, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FF6347. - /// - public static readonly TPixel Tomato = ColorBuilder.FromRgba(255, 99, 71, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFFFFF. - /// - public static readonly TPixel Transparent = ColorBuilder.FromRgba(255, 255, 255, 0); - - /// - /// Represents a matching the W3C definition that has an hex value of #40E0D0. - /// - public static readonly TPixel Turquoise = ColorBuilder.FromRgba(64, 224, 208, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #EE82EE. - /// - public static readonly TPixel Violet = ColorBuilder.FromRgba(238, 130, 238, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #F5DEB3. - /// - public static readonly TPixel Wheat = ColorBuilder.FromRgba(245, 222, 179, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFFFFF. - /// - public static readonly TPixel White = ColorBuilder.FromRgba(255, 255, 255, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #F5F5F5. - /// - public static readonly TPixel WhiteSmoke = ColorBuilder.FromRgba(245, 245, 245, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFFF00. - /// - public static readonly TPixel Yellow = ColorBuilder.FromRgba(255, 255, 0, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #9ACD32. - /// - public static readonly TPixel YellowGreen = ColorBuilder.FromRgba(154, 205, 50, 255); - - /// - /// Gets a collection of web safe, colors as defined in the CSS Color Module Level 4. - /// - public static TPixel[] WebSafePalette => WebSafePaletteLazy.Value; - - /// - /// Gets a collection of colors as defined in the original second edition of Werner’s Nomenclature of Colours 1821. - /// The hex codes were collected and defined by Nicholas Rougeux - /// - public static TPixel[] WernerPalette => WernerPaletteLazy.Value; - - private static TPixel[] GetWebSafePalette() => GetPalette(ColorConstants.WebSafeColors); - - private static TPixel[] GetWernerPalette() => GetPalette(ColorConstants.WernerColors); - - private static TPixel[] GetPalette(Rgba32[] palette) - { - var converted = new TPixel[palette.Length]; - - Span constantsBytes = MemoryMarshal.Cast(palette.AsSpan()); - PixelOperations.Instance.FromRgba32Bytes( - Configuration.Default, - constantsBytes, - converted, - palette.Length); - - return converted; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs index c58d17ef42..b439ef32c9 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs @@ -1,6 +1,9 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; +using System.Buffers.Binary; +using System.Globalization; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -233,7 +236,20 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// The . /// - public static Rgba32 FromHex(string hex) => ColorBuilder.FromHex(hex); + public static Rgba32 FromHex(string hex) + { + Guard.NotNullOrWhiteSpace(hex, nameof(hex)); + + hex = ToRgbaHex(hex); + + if (hex is null || !uint.TryParse(hex, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out uint packedValue)) + { + throw new ArgumentException("Hexadecimal string is not in the correct format.", nameof(hex)); + } + + packedValue = BinaryPrimitives.ReverseEndianness(packedValue); + return Unsafe.As(ref packedValue); + } /// public PixelOperations CreatePixelOperations() => new PixelOperations(); @@ -426,5 +442,42 @@ namespace SixLabors.ImageSharp.PixelFormats this.B = (byte)vector.Z; this.A = (byte)vector.W; } + + /// + /// Converts the specified hex value to an rrggbbaa hex value. + /// + /// The hex value to convert. + /// + /// A rrggbbaa hex value. + /// + private static string ToRgbaHex(string hex) + { + if (hex[0] == '#') + { + hex = hex.Substring(1); + } + + if (hex.Length == 8) + { + return hex; + } + + if (hex.Length == 6) + { + return hex + "FF"; + } + + if (hex.Length < 3 || hex.Length > 4) + { + return null; + } + + char r = hex[0]; + char g = hex[1]; + char b = hex[2]; + char a = hex.Length == 3 ? 'F' : hex[3]; + + return new string(new[] { r, r, g, g, b, b, a, a }); + } } } \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs index 96003cc5bf..38e75d1836 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs @@ -94,7 +94,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// The . /// - public static RgbaVector FromHex(string hex) => ColorBuilder.FromHex(hex); + public static RgbaVector FromHex(string hex) => Color.FromHex(hex).ToPixel(); /// public PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/Processing/DefaultInternalImageProcessorContext.cs b/src/ImageSharp/Processing/DefaultImageProcessorContext.cs similarity index 85% rename from src/ImageSharp/Processing/DefaultInternalImageProcessorContext.cs rename to src/ImageSharp/Processing/DefaultImageProcessorContext.cs index 091da003a2..d0c4c076f1 100644 --- a/src/ImageSharp/Processing/DefaultInternalImageProcessorContext.cs +++ b/src/ImageSharp/Processing/DefaultImageProcessorContext.cs @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Processing /// Performs processor application operations on the source image /// /// The pixel format - internal class DefaultInternalImageProcessorContext : IInternalImageProcessingContext + internal class DefaultImageProcessorContext : IInternalImageProcessingContext where TPixel : struct, IPixel { private readonly bool mutate; @@ -21,11 +21,11 @@ namespace SixLabors.ImageSharp.Processing private Image destination; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The image. /// The mutate. - public DefaultInternalImageProcessorContext(Image source, bool mutate) + public DefaultImageProcessorContext(Image source, bool mutate) { this.mutate = mutate; this.source = source; @@ -39,7 +39,7 @@ namespace SixLabors.ImageSharp.Processing public MemoryAllocator MemoryAllocator => this.source.GetConfiguration().MemoryAllocator; /// - public Image Apply() + public Image GetResultImage() { if (!this.mutate && this.destination is null) { @@ -65,8 +65,7 @@ namespace SixLabors.ImageSharp.Processing return this.ApplyProcessor(processorImplementation); } - /// - public IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle) + private IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle) { if (!this.mutate && this.destination is null) { @@ -86,8 +85,7 @@ namespace SixLabors.ImageSharp.Processing return this; } - /// - public IImageProcessingContext ApplyProcessor(IImageProcessor processor) + private IImageProcessingContext ApplyProcessor(IImageProcessor processor) { return this.ApplyProcessor(processor, this.GetCurrentBounds()); } diff --git a/src/ImageSharp/Processing/Extensions/BinaryDiffuseExtensions.cs b/src/ImageSharp/Processing/Extensions/BinaryDiffuseExtensions.cs index 7976daf25e..760102aacf 100644 --- a/src/ImageSharp/Processing/Extensions/BinaryDiffuseExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/BinaryDiffuseExtensions.cs @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Processing /// The image this method extends. /// The diffusion algorithm to apply. /// The threshold to apply binarization of the image. Must be between 0 and 1. - /// The to allow chaining of operations. + /// The to allow chaining of operations. public static IImageProcessingContext BinaryDiffuse( this IImageProcessingContext source, IErrorDiffuser diffuser, @@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The to allow chaining of operations. public static IImageProcessingContext BinaryDiffuse( this IImageProcessingContext source, IErrorDiffuser diffuser, @@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp.Processing /// The threshold to apply binarization of the image. Must be between 0 and 1. /// The color to use for pixels that are above the threshold. /// The color to use for pixels that are below the threshold - /// The to allow chaining of operations. + /// The to allow chaining of operations. public static IImageProcessingContext BinaryDiffuse( this IImageProcessingContext source, IErrorDiffuser diffuser, @@ -71,7 +71,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The to allow chaining of operations. public static IImageProcessingContext BinaryDiffuse( this IImageProcessingContext source, IErrorDiffuser diffuser, diff --git a/src/ImageSharp/Processing/Extensions/BinaryDitherExtensions.cs b/src/ImageSharp/Processing/Extensions/BinaryDitherExtensions.cs index b1e3d562ed..e8ce252a2d 100644 --- a/src/ImageSharp/Processing/Extensions/BinaryDitherExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/BinaryDitherExtensions.cs @@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image this method extends. /// The ordered ditherer. - /// The to allow chaining of operations. + /// The to allow chaining of operations. public static IImageProcessingContext BinaryDither(this IImageProcessingContext source, IOrderedDither dither) => source.ApplyProcessor(new BinaryOrderedDitherProcessor(dither)); @@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp.Processing /// The ordered ditherer. /// The color to use for pixels that are above the threshold. /// The color to use for pixels that are below the threshold - /// The to allow chaining of operations. + /// The to allow chaining of operations. public static IImageProcessingContext BinaryDither( this IImageProcessingContext source, IOrderedDither dither, @@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The to allow chaining of operations. public static IImageProcessingContext BinaryDither( this IImageProcessingContext source, IOrderedDither dither, @@ -63,7 +63,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The to allow chaining of operations. public static IImageProcessingContext BinaryDither( this IImageProcessingContext source, IOrderedDither dither, diff --git a/src/ImageSharp/Processing/Extensions/ProcessingExtensions.cs b/src/ImageSharp/Processing/Extensions/ProcessingExtensions.cs index e469af18b8..2304a44c39 100644 --- a/src/ImageSharp/Processing/Extensions/ProcessingExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/ProcessingExtensions.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; + using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors; @@ -13,18 +14,6 @@ namespace SixLabors.ImageSharp.Processing /// public static class ProcessingExtensions { - /// - /// Applies the given operation to the mutable image. - /// Useful when we need to extract information like Width/Height to parametrize the next operation working on the chain. - /// To achieve this the method actually implements an "inline" with as it's processing logic. - /// - /// The pixel format. - /// The image to mutate. - /// The operation to perform on the source. - /// The to allow chaining of operations. - public static IImageProcessingContext Apply(this IImageProcessingContext source, Action> operation) - where TPixel : struct, IPixel => source.ApplyProcessor(new DelegateProcessor(operation)); - /// /// Mutates the source image by applying the image operation to it. /// @@ -48,9 +37,9 @@ namespace SixLabors.ImageSharp.Processing Guard.NotNull(operation, nameof(operation)); Guard.NotNull(source, nameof(source)); - IInternalImageProcessingContext operationsRunner = source.GetConfiguration().ImageOperationsProvider.CreateImageProcessingContext(source, true); + IInternalImageProcessingContext operationsRunner = source.GetConfiguration().ImageOperationsProvider + .CreateImageProcessingContext(source, true); operation(operationsRunner); - operationsRunner.Apply(); } /// @@ -65,9 +54,9 @@ namespace SixLabors.ImageSharp.Processing Guard.NotNull(operations, nameof(operations)); Guard.NotNull(source, nameof(source)); - IInternalImageProcessingContext operationsRunner = source.GetConfiguration().ImageOperationsProvider.CreateImageProcessingContext(source, true); + IInternalImageProcessingContext operationsRunner = source.GetConfiguration().ImageOperationsProvider + .CreateImageProcessingContext(source, true); operationsRunner.ApplyProcessors(operations); - operationsRunner.Apply(); } /// @@ -96,9 +85,10 @@ namespace SixLabors.ImageSharp.Processing Guard.NotNull(operation, nameof(operation)); Guard.NotNull(source, nameof(source)); - IInternalImageProcessingContext operationsRunner = source.GetConfiguration().ImageOperationsProvider.CreateImageProcessingContext(source, false); + IInternalImageProcessingContext operationsRunner = source.GetConfiguration().ImageOperationsProvider + .CreateImageProcessingContext(source, false); operation(operationsRunner); - return operationsRunner.Apply(); + return operationsRunner.GetResultImage(); } /// @@ -114,9 +104,10 @@ namespace SixLabors.ImageSharp.Processing Guard.NotNull(operations, nameof(operations)); Guard.NotNull(source, nameof(source)); - IInternalImageProcessingContext operationsRunner = source.GetConfiguration().ImageOperationsProvider.CreateImageProcessingContext(source, false); + IInternalImageProcessingContext operationsRunner = source.GetConfiguration().ImageOperationsProvider + .CreateImageProcessingContext(source, false); operationsRunner.ApplyProcessors(operations); - return operationsRunner.Apply(); + return operationsRunner.GetResultImage(); } /// @@ -125,7 +116,9 @@ namespace SixLabors.ImageSharp.Processing /// The image processing context. /// The operations to perform on the source. /// The to allow chaining of operations. - public static IImageProcessingContext ApplyProcessors(this IImageProcessingContext source, params IImageProcessor[] operations) + public static IImageProcessingContext ApplyProcessors( + this IImageProcessingContext source, + params IImageProcessor[] operations) { foreach (IImageProcessor p in operations) { @@ -155,7 +148,7 @@ namespace SixLabors.ImageSharp.Processing IInternalImageProcessingContext operationsRunner = image.GetConfiguration() .ImageOperationsProvider.CreateImageProcessingContext(image, this.mutate); this.operation(operationsRunner); - this.ResultImage = operationsRunner.Apply(); + this.ResultImage = operationsRunner.GetResultImage(); } } } diff --git a/src/ImageSharp/Processing/IImageProcessingContextFactory.cs b/src/ImageSharp/Processing/IImageProcessingContextFactory.cs index 1ec2d191f3..948e70b445 100644 --- a/src/ImageSharp/Processing/IImageProcessingContextFactory.cs +++ b/src/ImageSharp/Processing/IImageProcessingContextFactory.cs @@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp.Processing public IInternalImageProcessingContext CreateImageProcessingContext(Image source, bool mutate) where TPixel : struct, IPixel { - return new DefaultInternalImageProcessorContext(source, mutate); + return new DefaultImageProcessorContext(source, mutate); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/IImageProcessingContext{TPixel}.cs b/src/ImageSharp/Processing/IImageProcessingContext{TPixel}.cs deleted file mode 100644 index b49cfe2151..0000000000 --- a/src/ImageSharp/Processing/IImageProcessingContext{TPixel}.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing.Processors; -using SixLabors.Primitives; - -namespace SixLabors.ImageSharp.Processing -{ - /// - /// A pixel-specific interface to queue up image operations to apply to an image. - /// - /// The pixel format - public interface IImageProcessingContext : IImageProcessingContext - where TPixel : struct, IPixel - { - /// - /// Adds the processor to the current set of image operations to be applied. - /// - /// The processor to apply. - /// The area to apply it to. - /// The current operations class to allow chaining of operations. - IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle); - - /// - /// Adds the processor to the current set of image operations to be applied. - /// - /// The processor to apply - /// The current operations class to allow chaining of operations. - IImageProcessingContext ApplyProcessor(IImageProcessor processor); - } - - /// - /// An internal interface to queue up image operations and have a method to apply them to and return a result - /// - /// The pixel format - internal interface IInternalImageProcessingContext : IImageProcessingContext - where TPixel : struct, IPixel - { - /// - /// Adds the processors to the current image - /// - /// The current image or a new image depending on whether this is allowed to mutate the source image. - Image Apply(); - } -} \ No newline at end of file diff --git a/src/ImageSharp/Processing/IInternalImageProcessingContext.cs b/src/ImageSharp/Processing/IInternalImageProcessingContext.cs new file mode 100644 index 0000000000..55303b1ef9 --- /dev/null +++ b/src/ImageSharp/Processing/IInternalImageProcessingContext.cs @@ -0,0 +1,22 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing +{ + /// + /// An interface for internal operations we don't want to expose on . + /// + /// The pixel type. + internal interface IInternalImageProcessingContext : IImageProcessingContext + where TPixel : struct, IPixel + { + /// + /// Returns the result image to return by + /// (and other overloads). + /// + /// The current image or a new image depending on whether it is requested to mutate the source image. + Image GetResultImage(); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/DelegateProcessor.cs b/src/ImageSharp/Processing/Processors/DelegateProcessor.cs deleted file mode 100644 index 7a9753d1a9..0000000000 --- a/src/ImageSharp/Processing/Processors/DelegateProcessor.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; - -namespace SixLabors.ImageSharp.Processing.Processors -{ - /// - /// Allows the application of processing algorithms to images via an action delegate - /// - /// The pixel format. - internal class DelegateProcessor : ImageProcessor - where TPixel : struct, IPixel - { - /// - /// Initializes a new instance of the class. - /// - /// The action. - public DelegateProcessor(Action> action) - { - this.Action = action; - } - - /// - /// Gets the action that will be applied to the image. - /// - internal Action> Action { get; } - - /// - protected override void BeforeImageApply(Image source, Rectangle sourceRectangle) - { - this.Action?.Invoke(source); - } - - /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - { - // NOP, we did all we wanted to do inside BeforeImageApply - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Dithering/AtkinsonDiffuser.cs b/src/ImageSharp/Processing/Processors/Dithering/AtkinsonDiffuser.cs index 17c97ddc9b..0461d179ff 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/AtkinsonDiffuser.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/AtkinsonDiffuser.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// Applies error diffusion based dithering using the Atkinson image dithering algorithm. /// /// - public sealed class AtkinsonDiffuser : ErrorDiffuserBase + public sealed class AtkinsonDiffuser : ErrorDiffuser { /// /// The diffusion matrix diff --git a/src/ImageSharp/Processing/Processors/Dithering/BurksDiffuser.cs b/src/ImageSharp/Processing/Processors/Dithering/BurksDiffuser.cs index 84455b24ad..23d4321e96 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/BurksDiffuser.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/BurksDiffuser.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// Applies error diffusion based dithering using the Burks image dithering algorithm. /// /// - public sealed class BurksDiffuser : ErrorDiffuserBase + public sealed class BurksDiffuser : ErrorDiffuser { /// /// The diffusion matrix diff --git a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffuserBase.cs b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffuser.cs similarity index 96% rename from src/ImageSharp/Processing/Processors/Dithering/ErrorDiffuserBase.cs rename to src/ImageSharp/Processing/Processors/Dithering/ErrorDiffuser.cs index 327828c39e..1c8156bf51 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffuserBase.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffuser.cs @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// /// The base class for performing error diffusion based dithering. /// - public abstract class ErrorDiffuserBase : IErrorDiffuser + public abstract class ErrorDiffuser : IErrorDiffuser { /// /// The vector to perform division. @@ -41,11 +41,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering private readonly DenseMatrix matrix; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The dithering matrix. /// The divisor. - internal ErrorDiffuserBase(in DenseMatrix matrix, byte divisor) + internal ErrorDiffuser(in DenseMatrix matrix, byte divisor) { Guard.MustBeGreaterThan(divisor, 0, nameof(divisor)); diff --git a/src/ImageSharp/Processing/Processors/Dithering/FloydSteinbergDiffuser.cs b/src/ImageSharp/Processing/Processors/Dithering/FloydSteinbergDiffuser.cs index 6a7655b593..78a28a693a 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/FloydSteinbergDiffuser.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/FloydSteinbergDiffuser.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// Applies error diffusion based dithering using the Floyd–Steinberg image dithering algorithm. /// /// - public sealed class FloydSteinbergDiffuser : ErrorDiffuserBase + public sealed class FloydSteinbergDiffuser : ErrorDiffuser { /// /// The diffusion matrix diff --git a/src/ImageSharp/Processing/Processors/Dithering/JarvisJudiceNinkeDiffuser.cs b/src/ImageSharp/Processing/Processors/Dithering/JarvisJudiceNinkeDiffuser.cs index a69557d6de..64c8610833 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/JarvisJudiceNinkeDiffuser.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/JarvisJudiceNinkeDiffuser.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// Applies error diffusion based dithering using the JarvisJudiceNinke image dithering algorithm. /// /// - public sealed class JarvisJudiceNinkeDiffuser : ErrorDiffuserBase + public sealed class JarvisJudiceNinkeDiffuser : ErrorDiffuser { /// /// The diffusion matrix diff --git a/src/ImageSharp/Processing/Processors/Dithering/Sierra2Diffuser.cs b/src/ImageSharp/Processing/Processors/Dithering/Sierra2Diffuser.cs index ebde2ceaf8..b489f8f28d 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/Sierra2Diffuser.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/Sierra2Diffuser.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// Applies error diffusion based dithering using the Sierra2 image dithering algorithm. /// /// - public sealed class Sierra2Diffuser : ErrorDiffuserBase + public sealed class Sierra2Diffuser : ErrorDiffuser { /// /// The diffusion matrix diff --git a/src/ImageSharp/Processing/Processors/Dithering/Sierra3Diffuser.cs b/src/ImageSharp/Processing/Processors/Dithering/Sierra3Diffuser.cs index 144a83a821..04abc782a6 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/Sierra3Diffuser.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/Sierra3Diffuser.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// Applies error diffusion based dithering using the Sierra3 image dithering algorithm. /// /// - public sealed class Sierra3Diffuser : ErrorDiffuserBase + public sealed class Sierra3Diffuser : ErrorDiffuser { /// /// The diffusion matrix diff --git a/src/ImageSharp/Processing/Processors/Dithering/SierraLiteDiffuser.cs b/src/ImageSharp/Processing/Processors/Dithering/SierraLiteDiffuser.cs index d71fba9f2e..2ac69cf456 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/SierraLiteDiffuser.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/SierraLiteDiffuser.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// Applies error diffusion based dithering using the SierraLite image dithering algorithm. /// /// - public sealed class SierraLiteDiffuser : ErrorDiffuserBase + public sealed class SierraLiteDiffuser : ErrorDiffuser { /// /// The diffusion matrix diff --git a/src/ImageSharp/Processing/Processors/Dithering/StevensonArceDiffuser.cs b/src/ImageSharp/Processing/Processors/Dithering/StevensonArceDiffuser.cs index 4b1323065f..b929a28d30 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/StevensonArceDiffuser.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/StevensonArceDiffuser.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// /// Applies error diffusion based dithering using the Stevenson-Arce image dithering algorithm. /// - public sealed class StevensonArceDiffuser : ErrorDiffuserBase + public sealed class StevensonArceDiffuser : ErrorDiffuser { /// /// The diffusion matrix diff --git a/src/ImageSharp/Processing/Processors/Dithering/StuckiDiffuser.cs b/src/ImageSharp/Processing/Processors/Dithering/StuckiDiffuser.cs index 1dd510a5ec..bb3aebc3f5 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/StuckiDiffuser.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/StuckiDiffuser.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// Applies error diffusion based dithering using the Stucki image dithering algorithm. /// /// - public sealed class StuckiDiffuser : ErrorDiffuserBase + public sealed class StuckiDiffuser : ErrorDiffuser { /// /// The diffusion matrix diff --git a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs index 5a043cb207..9625ca216e 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs @@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// Provides the base methods to perform affine transforms on an image. /// /// The pixel format. - internal class AffineTransformProcessor : TransformProcessorBase + internal class AffineTransformProcessor : TransformProcessor where TPixel : struct, IPixel { public AffineTransformProcessor(AffineTransformProcessor definition) diff --git a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs index 9bddda3825..4c4c27defa 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs @@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// Provides methods to allow the cropping of an image. /// /// The pixel format. - internal class CropProcessor : TransformProcessorBase + internal class CropProcessor : TransformProcessor where TPixel : struct, IPixel { private readonly CropProcessor definition; diff --git a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs index ab07040f71..15d8141baa 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs @@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// Provides the base methods to perform non-affine transforms on an image. /// /// The pixel format. - internal class ProjectiveTransformProcessor : TransformProcessorBase + internal class ProjectiveTransformProcessor : TransformProcessor where TPixel : struct, IPixel { private readonly ProjectiveTransformProcessor definition; diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs index cf99f28dd7..fdd1d339dd 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// The original code has been adapted from . /// /// The pixel format. - internal class ResizeProcessor : TransformProcessorBase + internal class ResizeProcessor : TransformProcessor where TPixel : struct, IPixel { // The following fields are not immutable but are optionally created on demand. diff --git a/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorBase.cs b/src/ImageSharp/Processing/Processors/Transforms/TransformProcessor.cs similarity index 89% rename from src/ImageSharp/Processing/Processors/Transforms/TransformProcessorBase.cs rename to src/ImageSharp/Processing/Processors/Transforms/TransformProcessor.cs index 286ada2e57..ef508549ac 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorBase.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/TransformProcessor.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// The base class for all transform processors. Any processor that changes the dimensions of the image should inherit from this. /// /// The pixel format. - internal abstract class TransformProcessorBase : CloningImageProcessor + internal abstract class TransformProcessor : CloningImageProcessor where TPixel : struct, IPixel { /// diff --git a/tests/ImageSharp.Benchmarks/Samplers/Glow.cs b/tests/ImageSharp.Benchmarks/Samplers/Glow.cs index 29f69cdd55..263ce6a7e4 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/Glow.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/Glow.cs @@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Benchmarks public void Setup() { this.bulk = new GlowProcessor(Color.Beige, 800 * .5f, GraphicsOptions.Default); - this.parallel = new GlowProcessorParallel(NamedColors.Beige) { Radius = 800 * .5f, }; + this.parallel = new GlowProcessorParallel(Color.Beige) { Radius = 800 * .5f, }; } [Benchmark(Description = "ImageSharp Glow - Bulk")] diff --git a/tests/ImageSharp.Tests/BaseImageOperationsExtensionTest.cs b/tests/ImageSharp.Tests/BaseImageOperationsExtensionTest.cs index 857c19d87f..d3de04354c 100644 --- a/tests/ImageSharp.Tests/BaseImageOperationsExtensionTest.cs +++ b/tests/ImageSharp.Tests/BaseImageOperationsExtensionTest.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Tests { public abstract class BaseImageOperationsExtensionTest { - protected readonly IImageProcessingContext operations; + protected readonly IImageProcessingContext operations; private readonly FakeImageOperationsProvider.FakeImageOperations internalOperations; protected readonly Rectangle rect; protected readonly GraphicsOptions options; diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/ApproximateColorspaceComparer.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/ApproximateColorspaceComparer.cs index 57da2ff170..7bf84dd0af 100644 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/ApproximateColorspaceComparer.cs +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/ApproximateColorspaceComparer.cs @@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion IEqualityComparer, IEqualityComparer, IEqualityComparer, - IEqualityComparer + IEqualityComparer { private readonly float Epsilon; @@ -222,14 +222,14 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion public int GetHashCode(GammaWorkingSpace obj) => obj.GetHashCode(); /// - public bool Equals(RgbWorkingSpaceBase x, RgbWorkingSpaceBase y) + public bool Equals(RgbWorkingSpace x, RgbWorkingSpace y) { return this.Equals(x.WhitePoint, y.WhitePoint) && this.Equals(x.ChromaticityCoordinates, y.ChromaticityCoordinates); } /// - public int GetHashCode(RgbWorkingSpaceBase obj) => obj.GetHashCode(); + public int GetHashCode(RgbWorkingSpace obj) => obj.GetHashCode(); private bool Equals(float x, float y) { diff --git a/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs b/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs index c91cb16d46..c3ae0cddff 100644 --- a/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs +++ b/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs @@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Tests public MemoryAllocator MemoryAllocator => this.Source.GetConfiguration().MemoryAllocator; - public Image Apply() + public Image GetResultImage() { return this.Source; } @@ -86,25 +86,6 @@ namespace SixLabors.ImageSharp.Tests return this; } - public IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle) - { - this.Applied.Add(new AppliedOperation - { - GenericProcessor = processor, - Rectangle = rectangle - }); - return this; - } - - public IImageProcessingContext ApplyProcessor(IImageProcessor processor) - { - this.Applied.Add(new AppliedOperation - { - GenericProcessor = processor - }); - return this; - } - public struct AppliedOperation { public Rectangle? Rectangle { get; set; } diff --git a/tests/ImageSharp.Tests/Image/ImageProcessingContextTests.cs b/tests/ImageSharp.Tests/Image/ImageProcessingContextTests.cs deleted file mode 100644 index 6dadc6e7a2..0000000000 --- a/tests/ImageSharp.Tests/Image/ImageProcessingContextTests.cs +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.Primitives; -using Xunit; - -namespace SixLabors.ImageSharp.Tests -{ - public class ImageProcessingContextTests - { - // [Fact] - // public void MutatedSizeIsAccuratePerOperation() - // { - // var x500 = new Size(500, 500); - // var x400 = new Size(400, 400); - // var x300 = new Size(300, 300); - // var x200 = new Size(200, 200); - // var x100 = new Size(100, 100); - // using (var image = new Image(500, 500)) - // { - // image.Mutate(x => - // x.AssertSize(x500) - // .Resize(x400).AssertSize(x400) - // .Resize(x300).AssertSize(x300) - // .Resize(x200).AssertSize(x200) - // .Resize(x100).AssertSize(x100)); - // } - // } - // - // [Fact] - // public void ClonedSizeIsAccuratePerOperation() - // { - // var x500 = new Size(500, 500); - // var x400 = new Size(400, 400); - // var x300 = new Size(300, 300); - // var x200 = new Size(200, 200); - // var x100 = new Size(100, 100); - // using (var image = new Image(500, 500)) - // { - // image.Clone(x => - // x.AssertSize(x500) - // .Resize(x400).AssertSize(x400) - // .Resize(x300).AssertSize(x300) - // .Resize(x200).AssertSize(x200) - // .Resize(x100).AssertSize(x100)); - // } - // } - } - - public static class SizeAssertationExtensions - { - public static IImageProcessingContext AssertSize(this IImageProcessingContext context, Size size) - { - Assert.Equal(size, context.GetCurrentSize()); - return context; - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs b/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs index d51470292d..ea99573141 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs @@ -113,8 +113,8 @@ namespace SixLabors.ImageSharp.Tests using (var memoryManager = new BitmapMemoryManager(bmp)) { Memory memory = memoryManager.Memory; - Bgra32 bg = NamedColors.Red; - Bgra32 fg = NamedColors.Green; + Bgra32 bg = Color.Red; + Bgra32 fg = Color.Green; using (var image = Image.WrapMemory(memory, bmp.Width, bmp.Height)) { @@ -144,8 +144,8 @@ namespace SixLabors.ImageSharp.Tests using (var bmp = new Bitmap(51, 23)) { var memoryManager = new BitmapMemoryManager(bmp); - Bgra32 bg = NamedColors.Red; - Bgra32 fg = NamedColors.Green; + Bgra32 bg = Color.Red; + Bgra32 fg = Color.Green; using (var image = Image.WrapMemory(memoryManager, bmp.Width, bmp.Height)) { diff --git a/tests/ImageSharp.Tests/PixelFormats/ColorBuilderTests.cs b/tests/ImageSharp.Tests/PixelFormats/ColorBuilderTests.cs deleted file mode 100644 index e56cac2794..0000000000 --- a/tests/ImageSharp.Tests/PixelFormats/ColorBuilderTests.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using SixLabors.ImageSharp.PixelFormats; - -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Colors -{ - public class ColorBuilderTests - { - [Fact] - public void ParseShortHex() - { - Assert.Equal(new Rgb24(255, 255, 255), ColorBuilder.FromHex("#fff")); - Assert.Equal(new Rgb24(255, 255, 255), ColorBuilder.FromHex("fff")); - Assert.Equal(new Rgba32(0, 0, 0, 255), ColorBuilder.FromHex("000f")); - } - - [Fact] - public void ParseHexLeadingPoundIsOptional() - { - Assert.Equal(new Rgb24(0, 128, 128), ColorBuilder.FromHex("#008080")); - Assert.Equal(new Rgb24(0, 128, 128), ColorBuilder.FromHex("008080")); - } - - [Fact] - public void ParseHexThrowsOnEmpty() - { - Assert.Throws(() => ColorBuilder.FromHex("")); - } - - [Fact] - public void ParseHexThrowsOnNull() - { - Assert.Throws(() => ColorBuilder.FromHex(null)); - } - } -} diff --git a/tests/ImageSharp.Tests/PixelFormats/ColorDefinitionTests.cs b/tests/ImageSharp.Tests/PixelFormats/ColorDefinitionTests.cs deleted file mode 100644 index 302e56ec71..0000000000 --- a/tests/ImageSharp.Tests/PixelFormats/ColorDefinitionTests.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using SixLabors.ImageSharp.PixelFormats; -using Xunit; - -namespace SixLabors.ImageSharp.Tests -{ - public class ColorDefinitionTests - { - public static TheoryData ColorNames - { - get - { - var result = new TheoryData(); - foreach (string name in typeof(NamedColors).GetTypeInfo() - .GetFields().Where(x => x.Name != nameof(NamedColors.WebSafePalette)).Select(x => x.Name)) - { - result.Add(name); - } - return result; - } - } - - [Theory] - [MemberData(nameof(ColorNames))] - public void AllColorsAreOnGenericAndBaseColor(string name) - { - FieldInfo generic = typeof(NamedColors).GetTypeInfo().GetField(name); - FieldInfo specific = typeof(Rgba32).GetTypeInfo().GetField(name); - - Assert.NotNull(specific); - Assert.NotNull(generic); - Assert.True(specific.Attributes.HasFlag(FieldAttributes.Public), "specific must be public"); - Assert.True(specific.Attributes.HasFlag(FieldAttributes.Static), "specific must be static"); - Assert.True(generic.Attributes.HasFlag(FieldAttributes.Public), "generic must be public"); - Assert.True(generic.Attributes.HasFlag(FieldAttributes.Static), "generic must be static"); - Rgba32 expected = (Rgba32)generic.GetValue(null); - Rgba32 actual = (Rgba32)specific.GetValue(null); - Assert.Equal(expected, actual); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/DelegateTest.cs b/tests/ImageSharp.Tests/Processing/DelegateTest.cs deleted file mode 100644 index 73d3c80230..0000000000 --- a/tests/ImageSharp.Tests/Processing/DelegateTest.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Processing -{ - using SixLabors.ImageSharp.Processing.Processors; - - public class DelegateTest : BaseImageOperationsExtensionTest - { - [Fact] - public void Run_CreatedDelegateProcessor() - { - Action> action = (i) => { }; - this.operations.Apply(action); - - DelegateProcessor processor = this.Verify>(); - Assert.Equal(action, processor.Action); - } - } -} diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BasicTestPatternProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BasicTestPatternProvider.cs index de203535c6..752f8e18bd 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BasicTestPatternProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BasicTestPatternProvider.cs @@ -31,9 +31,9 @@ namespace SixLabors.ImageSharp.Tests { var result = new Image(this.Configuration, this.Width, this.Height); - TPixel topLeftColor = NamedColors.Red; - TPixel topRightColor = NamedColors.Green; - TPixel bottomLeftColor = NamedColors.Blue; + TPixel topLeftColor = Color.Red.ToPixel(); + TPixel topRightColor = Color.Green.ToPixel(); + TPixel bottomLeftColor = Color.Blue.ToPixel(); // Transparent purple: TPixel bottomRightColor = default; @@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp.Tests Span row = result.GetPixelRowSpan(y); row.Slice(0, midX).Fill(topLeftColor); - row.Slice(midX, this.Width-midX).Fill(topRightColor); + row.Slice(midX, this.Width - midX).Fill(topRightColor); } for (int y = midY; y < this.Height; y++) @@ -55,7 +55,7 @@ namespace SixLabors.ImageSharp.Tests Span row = result.GetPixelRowSpan(y); row.Slice(0, midX).Fill(bottomLeftColor); - row.Slice(midX, this.Width-midX).Fill(bottomRightColor); + row.Slice(midX, this.Width - midX).Fill(bottomRightColor); } return result; diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs index 6df8c85016..94b2b33913 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs @@ -21,6 +21,16 @@ namespace SixLabors.ImageSharp.Tests { static readonly Dictionary> TestImages = new Dictionary>(); + private static TPixel[] BlackWhitePixels = new[] { + Color.Black.ToPixel(), + Color.White.ToPixel() + }; + + private static TPixel[] PinkBluePixels = new[] { + Color.HotPink.ToPixel(), + Color.Blue.ToPixel() + }; + public TestPatternProvider(int width, int height) : base(width, height) { @@ -80,12 +90,6 @@ namespace SixLabors.ImageSharp.Tests stride = 1; } - TPixel[] c = - { - NamedColors.HotPink, - NamedColors.Blue - }; - for (int y = top; y < bottom; y++) { int p = 0; @@ -94,9 +98,9 @@ namespace SixLabors.ImageSharp.Tests if (x % stride == 0) { p++; - p = p % c.Length; + p = p % PinkBluePixels.Length; } - pixels[x, y] = c[p]; + pixels[x, y] = PinkBluePixels[p]; } } } @@ -113,11 +117,6 @@ namespace SixLabors.ImageSharp.Tests int top = 0; int bottom = pixels.Height / 2; int stride = pixels.Width / 6; - TPixel[] c = - { - NamedColors.Black, - NamedColors.White - }; int p = 0; for (int y = top; y < bottom; y++) @@ -125,7 +124,7 @@ namespace SixLabors.ImageSharp.Tests if (y % stride == 0) { p++; - p = p % c.Length; + p = p % BlackWhitePixels.Length; } int pstart = p; for (int x = left; x < right; x++) @@ -133,9 +132,9 @@ namespace SixLabors.ImageSharp.Tests if (x % stride == 0) { p++; - p = p % c.Length; + p = p % BlackWhitePixels.Length; } - pixels[x, y] = c[p]; + pixels[x, y] = BlackWhitePixels[p]; } p = pstart; } From 561b0a619c0cbee4134af83bbf73f55d296f389a Mon Sep 17 00:00:00 2001 From: Brian Popow <38701097+brianpopow@users.noreply.github.com> Date: Sat, 25 May 2019 02:35:02 +0200 Subject: [PATCH 207/223] Fix for Issue #871 and #914 (#915) --- .../PixelFormats/PixelImplementations/Rgba32.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs index b439ef32c9..ba259ca8ed 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs @@ -136,13 +136,16 @@ namespace SixLabors.ImageSharp.PixelFormats /// public Rgb24 Rgb { - // If this is changed to ShortMethod then several jpeg encoding tests fail - // on 32 bit Net 4.6.2 and NET 4.7.1 - [MethodImpl(InliningOptions.ColdPath)] - get => Unsafe.As(ref this); + [MethodImpl(InliningOptions.ShortMethod)] + get => new Rgb24(this.R, this.G, this.B); [MethodImpl(InliningOptions.ShortMethod)] - set => Unsafe.As(ref this) = value; + set + { + this.R = value.R; + this.G = value.G; + this.B = value.B; + } } /// From 5d3492201c78b4dd8fc409eeca9314713f85a393 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 25 May 2019 22:43:14 +1000 Subject: [PATCH 208/223] Fix #466 (#916) --- .../Processing/Extensions/PadExtensions.cs | 14 ++++++-- .../Processors/Transforms/PadTest.cs | 33 ++++++++++++++++--- 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/src/ImageSharp/Processing/Extensions/PadExtensions.cs b/src/ImageSharp/Processing/Extensions/PadExtensions.cs index 29f80b43ff..270380084f 100644 --- a/src/ImageSharp/Processing/Extensions/PadExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/PadExtensions.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing @@ -20,6 +19,17 @@ namespace SixLabors.ImageSharp.Processing /// The new height. /// The to allow chaining of operations. public static IImageProcessingContext Pad(this IImageProcessingContext source, int width, int height) + => source.Pad(width, height, default); + + /// + /// Evenly pads an image to fit the new dimensions with the given background color. + /// + /// The source image to pad. + /// The new width. + /// The new height. + /// The background color with which to pad the image. + /// The to allow chaining of operations. + public static IImageProcessingContext Pad(this IImageProcessingContext source, int width, int height, Color color) { var options = new ResizeOptions { @@ -28,7 +38,7 @@ namespace SixLabors.ImageSharp.Processing Sampler = KnownResamplers.NearestNeighbor, }; - return source.Resize(options); + return color.Equals(default) ? source.Resize(options) : source.Resize(options).BackgroundColor(color); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/PadTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/PadTest.cs index 750d67c32a..f568278706 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/PadTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/PadTest.cs @@ -10,10 +10,10 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms public class PadTest { public static readonly string[] CommonTestImages = - { - TestImages.Png.CalliphoraPartial, TestImages.Png.Bike - }; - + { + TestImages.Png.CalliphoraPartial, TestImages.Png.Bike + }; + [Theory] [WithFileCollection(nameof(CommonTestImages), PixelTypes.Rgba32)] public void ImageShouldPad(TestImageProvider provider) @@ -29,7 +29,30 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms { for (int x = 0; x < 25; x++) { - Assert.Equal(default(TPixel), image[x, y]); + Assert.Equal(default, image[x, y]); + } + } + } + } + + [Theory] + [WithFileCollection(nameof(CommonTestImages), PixelTypes.Rgba32)] + public void ImageShouldPadWithBackgroundColor(TestImageProvider provider) + where TPixel : struct, IPixel + { + Color color = Color.Red; + TPixel expected = color.ToPixel(); + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Pad(image.Width + 50, image.Height + 50, color)); + image.DebugSave(provider); + + // Check pixels are filled + for (int y = 0; y < 25; y++) + { + for (int x = 0; x < 25; x++) + { + Assert.Equal(expected, image[x, y]); } } } From c5319d1ba429a4791fa264811de61956561cefc3 Mon Sep 17 00:00:00 2001 From: Dirk Lemstra Date: Sat, 25 May 2019 23:24:44 +0200 Subject: [PATCH 209/223] Added funding file with a link to open collective. --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000000..ee7a10862e --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +open_collective: imagesharp \ No newline at end of file From 35e2f78a32a8c4aceebf25d45a2750e12f0a9571 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 27 May 2019 20:55:15 +1000 Subject: [PATCH 210/223] Removes usage of linq in several critical paths (#918) * Remove linq usage from jpeg + formatting * png * ICC + formattiing * Resize * Fix base class comparison. --- .../Components/Decoder/ProfileResolver.cs | 8 ++-- .../Formats/Jpeg/JpegDecoderCore.cs | 3 +- .../Formats/Jpeg/JpegEncoderCore.cs | 36 +++++++------- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 5 +- .../ICC/Curves/IccOneDimensionalCurve.cs | 3 +- .../Profiles/ICC/Curves/IccResponseCurve.cs | 8 +--- .../DataWriter/IccDataWriter.TagDataEntry.cs | 48 +++++-------------- .../MetaData/Profiles/ICC/IccWriter.cs | 1 + .../IccCurveSetProcessElement.cs | 12 ++--- .../IccChromaticityTagDataEntry.cs | 12 ++--- .../IccColorantTableTagDataEntry.cs | 18 ++----- .../TagDataEntries/IccLut16TagDataEntry.cs | 19 +++----- .../ICC/TagDataEntries/IccLut8TagDataEntry.cs | 14 ++---- .../TagDataEntries/IccLutAToBTagDataEntry.cs | 44 +++++++---------- .../TagDataEntries/IccLutBToATagDataEntry.cs | 37 +++++--------- .../IccMultiLocalizedUnicodeTagDataEntry.cs | 3 +- .../IccMultiProcessElementsTagDataEntry.cs | 12 ++--- .../IccNamedColor2TagDataEntry.cs | 2 +- .../IccProfileSequenceDescTagDataEntry.cs | 15 ++---- .../IccResponseCurveSet16TagDataEntry.cs | 12 ++--- .../IccUInt64ArrayTagDataEntry.cs | 16 ++----- .../MetaData/Profiles/ICC/Various/IccClut.cs | 5 +- .../Profiles/ICC/Various/IccNamedColor.cs | 20 ++------ .../ICC/Various/IccProfileDescription.cs | 18 +++---- .../Various/IccProfileSequenceIdentifier.cs | 10 ++-- src/ImageSharp/Processing/ResizeOptions.cs | 4 +- 26 files changed, 122 insertions(+), 263 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ProfileResolver.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ProfileResolver.cs index abfd6f556c..54633a5d72 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ProfileResolver.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ProfileResolver.cs @@ -12,17 +12,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder internal static class ProfileResolver { /// - /// Describes the EXIF specific markers. + /// Describes the JFIF specific markers. /// public static readonly byte[] JFifMarker = Encoding.ASCII.GetBytes("JFIF\0"); /// - /// Describes the EXIF specific markers. + /// Describes the ICC specific markers. /// public static readonly byte[] IccMarker = Encoding.ASCII.GetBytes("ICC_PROFILE\0"); /// - /// Describes the ICC specific markers. + /// Describes the EXIF specific markers. /// public static readonly byte[] ExifMarker = Encoding.ASCII.GetBytes("Exif\0\0"); @@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// /// The bytes to check. /// The profile identifier. - /// The + /// The . public static bool IsProfile(ReadOnlySpan bytesToCheck, ReadOnlySpan profileIdentifier) { return bytesToCheck.Length >= profileIdentifier.Length diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 8bd6514e04..fd0c289b88 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -4,7 +4,6 @@ using System; using System.Buffers.Binary; using System.IO; -using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Common.Helpers; @@ -726,7 +725,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg this.InputStream.Read(this.temp, 0, length); // We only support 8-bit and 12-bit precision. - if (!this.supportedPrecisions.Contains(this.temp[0])) + if (Array.IndexOf(this.supportedPrecisions, this.temp[0]) == -1) { JpegThrowHelper.ThrowImageFormatException("Only 8-Bit and 12-Bit precision supported."); } diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs index d4ce28107f..0b9eeb609b 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs @@ -4,7 +4,6 @@ using System; using System.Buffers.Binary; using System.IO; -using System.Linq; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Formats.Jpeg.Components; @@ -197,7 +196,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); - ushort max = JpegConstants.MaxLength; + const ushort max = JpegConstants.MaxLength; if (image.Width >= max || image.Height >= max) { throw new ImageFormatException($"Image is too large to encode at {image.Width}x{image.Height}."); @@ -226,7 +225,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg InitQuantizationTable(1, scale, ref this.chrominanceQuantTable); // Compute number of components based on input image type. - int componentCount = 3; + const int componentCount = 3; // Write the Start Of Image marker. this.WriteApplicationHeader(metadata); @@ -278,7 +277,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg private static void InitQuantizationTable(int i, int scale, ref Block8x8F quant) { DebugGuard.MustBeBetweenOrEqualTo(i, 0, 1, nameof(i)); - var unscaledQuant = (i == 0) ? UnscaledQuant_Luminance : UnscaledQuant_Chrominance; + ReadOnlySpan unscaledQuant = (i == 0) ? UnscaledQuant_Luminance : UnscaledQuant_Chrominance; for (int j = 0; j < Block8x8F.Size; j++) { @@ -653,8 +652,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg return; } - const int MaxBytesApp1 = 65533; - const int MaxBytesWithExifId = 65527; + const int MaxBytesApp1 = 65533; // 64k - 2 padding bytes + const int MaxBytesWithExifId = 65527; // Max - 6 bytes for EXIF header. byte[] data = exifProfile?.ToByteArray(); @@ -663,31 +662,30 @@ namespace SixLabors.ImageSharp.Formats.Jpeg return; } - data = ProfileResolver.ExifMarker.Concat(data).ToArray(); - - int remaining = data.Length; + // We can write up to a maximum of 64 data to the initial marker so calculate boundaries. + int exifMarkerLength = ProfileResolver.ExifMarker.Length; + int remaining = exifMarkerLength + data.Length; int bytesToWrite = remaining > MaxBytesApp1 ? MaxBytesApp1 : remaining; int app1Length = bytesToWrite + 2; + // Write the app marker, EXIF marker, and data this.WriteApp1Header(app1Length); - - // write the exif data - this.outputStream.Write(data, 0, bytesToWrite); + this.outputStream.Write(ProfileResolver.ExifMarker); + this.outputStream.Write(data, 0, bytesToWrite - exifMarkerLength); remaining -= bytesToWrite; - // if the exif data exceeds 64K, write it in multiple APP1 Markers - for (int idx = MaxBytesApp1; idx < data.Length; idx += MaxBytesWithExifId) + // If the exif data exceeds 64K, write it in multiple APP1 Markers + for (int idx = MaxBytesWithExifId; idx < data.Length; idx += MaxBytesWithExifId) { bytesToWrite = remaining > MaxBytesWithExifId ? MaxBytesWithExifId : remaining; - app1Length = bytesToWrite + 2 + 6; + app1Length = bytesToWrite + 2 + exifMarkerLength; this.WriteApp1Header(app1Length); - // write Exif00 marker - ProfileResolver.ExifMarker.AsSpan().CopyTo(this.buffer.AsSpan()); - this.outputStream.Write(this.buffer, 0, 6); + // Write Exif00 marker + this.outputStream.Write(ProfileResolver.ExifMarker); - // write the exif data + // Write the exif data this.outputStream.Write(data, idx, bytesToWrite); remaining -= bytesToWrite; diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index cbc35f3c3e..def57c3b0e 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -6,7 +6,6 @@ using System.Buffers; using System.Buffers.Binary; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Advanced; @@ -231,7 +230,7 @@ namespace SixLabors.ImageSharp.Formats.Png if (this.pngColorType == PngColorType.Palette) { byte bits = (byte)this.pngBitDepth; - if (!ColorTypes[this.pngColorType.Value].Contains(bits)) + if (Array.IndexOf(ColorTypes[this.pngColorType.Value], bits) == -1) { throw new NotSupportedException("Bit depth is not supported or not valid."); } @@ -268,7 +267,7 @@ namespace SixLabors.ImageSharp.Formats.Png else { this.bitDepth = (byte)this.pngBitDepth; - if (!ColorTypes[this.pngColorType.Value].Contains(this.bitDepth)) + if (Array.IndexOf(ColorTypes[this.pngColorType.Value], this.bitDepth) == -1) { throw new NotSupportedException("Bit depth is not supported or not valid."); } diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccOneDimensionalCurve.cs b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccOneDimensionalCurve.cs index e1d362a7fe..cbb433012d 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccOneDimensionalCurve.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccOneDimensionalCurve.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Linq; namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { @@ -52,7 +51,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc } return this.BreakPoints.AsSpan().SequenceEqual(other.BreakPoints) - && this.Segments.SequenceEqual(other.Segments); + && this.Segments.AsSpan().SequenceEqual(other.Segments); } } } diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccResponseCurve.cs b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccResponseCurve.cs index 4f0345d352..b9a50acd47 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccResponseCurve.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccResponseCurve.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Linq; using System.Numerics; namespace SixLabors.ImageSharp.Metadata.Profiles.Icc @@ -65,10 +64,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc } /// - public override bool Equals(object obj) - { - return obj is IccResponseCurve other && this.Equals(other); - } + public override bool Equals(object obj) => obj is IccResponseCurve other && this.Equals(other); /// public override int GetHashCode() @@ -88,7 +84,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc for (int i = 0; i < this.ResponseArrays.Length; i++) { - if (!this.ResponseArrays[i].SequenceEqual(other.ResponseArrays[i])) + if (!this.ResponseArrays[i].AsSpan().SequenceEqual(other.ResponseArrays[i])) { return false; } diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntry.cs index 13fb023d38..f3c632a869 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntry.cs @@ -163,10 +163,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc /// /// The entry to write /// The number of bytes written - public int WriteUnknownTagDataEntry(IccUnknownTagDataEntry value) - { - return this.WriteArray(value.Data); - } + public int WriteUnknownTagDataEntry(IccUnknownTagDataEntry value) => this.WriteArray(value.Data); /// /// Writes a @@ -269,10 +266,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc /// /// The entry to write /// The number of bytes written - public int WriteDateTimeTagDataEntry(IccDateTimeTagDataEntry value) - { - return this.WriteDateTime(value.Value); - } + public int WriteDateTimeTagDataEntry(IccDateTimeTagDataEntry value) => this.WriteDateTime(value.Value); /// /// Writes a @@ -563,6 +557,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc long tpos = this.dataStream.Position; this.dataStream.Position += cultureCount * 12; + // TODO: Investigate cost of Linq GroupBy IGrouping[] texts = value.Texts.GroupBy(t => t.Text).ToArray(); uint[] offset = new uint[texts.Length]; @@ -625,7 +620,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc long tpos = this.dataStream.Position; this.dataStream.Position += value.Data.Length * 8; - IccPositionNumber[] posTable = new IccPositionNumber[value.Data.Length]; + var posTable = new IccPositionNumber[value.Data.Length]; for (int i = 0; i < value.Data.Length; i++) { uint offset = (uint)(this.dataStream.Position - start); @@ -673,10 +668,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc /// /// The entry to write /// The number of bytes written - public int WriteParametricCurveTagDataEntry(IccParametricCurveTagDataEntry value) - { - return this.WriteParametricCurve(value.Curve); - } + public int WriteParametricCurveTagDataEntry(IccParametricCurveTagDataEntry value) => this.WriteParametricCurve(value.Curve); /// /// Writes a @@ -793,20 +785,14 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc /// /// The entry to write /// The number of bytes written - public int WriteSignatureTagDataEntry(IccSignatureTagDataEntry value) - { - return this.WriteAsciiString(value.SignatureData, 4, false); - } + public int WriteSignatureTagDataEntry(IccSignatureTagDataEntry value) => this.WriteAsciiString(value.SignatureData, 4, false); /// /// Writes a /// /// The entry to write /// The number of bytes written - public int WriteTextTagDataEntry(IccTextTagDataEntry value) - { - return this.WriteAsciiString(value.Text); - } + public int WriteTextTagDataEntry(IccTextTagDataEntry value) => this.WriteAsciiString(value.Text); /// /// Writes a @@ -829,40 +815,28 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc /// /// The entry to write /// The number of bytes written - public int WriteUInt16ArrayTagDataEntry(IccUInt16ArrayTagDataEntry value) - { - return this.WriteArray(value.Data); - } + public int WriteUInt16ArrayTagDataEntry(IccUInt16ArrayTagDataEntry value) => this.WriteArray(value.Data); /// /// Writes a /// /// The entry to write /// The number of bytes written - public int WriteUInt32ArrayTagDataEntry(IccUInt32ArrayTagDataEntry value) - { - return this.WriteArray(value.Data); - } + public int WriteUInt32ArrayTagDataEntry(IccUInt32ArrayTagDataEntry value) => this.WriteArray(value.Data); /// /// Writes a /// /// The entry to write /// The number of bytes written - public int WriteUInt64ArrayTagDataEntry(IccUInt64ArrayTagDataEntry value) - { - return this.WriteArray(value.Data); - } + public int WriteUInt64ArrayTagDataEntry(IccUInt64ArrayTagDataEntry value) => this.WriteArray(value.Data); /// /// Writes a /// /// The entry to write /// The number of bytes written - public int WriteUInt8ArrayTagDataEntry(IccUInt8ArrayTagDataEntry value) - { - return this.WriteArray(value.Data); - } + public int WriteUInt8ArrayTagDataEntry(IccUInt8ArrayTagDataEntry value) => this.WriteArray(value.Data); /// /// Writes a diff --git a/src/ImageSharp/MetaData/Profiles/ICC/IccWriter.cs b/src/ImageSharp/MetaData/Profiles/ICC/IccWriter.cs index b5a7c830dd..186f06df05 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/IccWriter.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/IccWriter.cs @@ -70,6 +70,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc private IccTagTableEntry[] WriteTagData(IccDataWriter writer, IccTagDataEntry[] entries) { + // TODO: Investigate cost of Linq GroupBy IEnumerable> grouped = entries.GroupBy(t => t); // (Header size) + (entry count) + (nr of entries) * (size of table entry) diff --git a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccCurveSetProcessElement.cs b/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccCurveSetProcessElement.cs index d72aff3c92..c6409e3c1d 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccCurveSetProcessElement.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccCurveSetProcessElement.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Linq; namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { @@ -17,9 +16,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc /// An array with one dimensional curves public IccCurveSetProcessElement(IccOneDimensionalCurve[] curves) : base(IccMultiProcessElementSignature.CurveSet, curves?.Length ?? 1, curves?.Length ?? 1) - { - this.Curves = curves ?? throw new ArgumentNullException(nameof(curves)); - } + => this.Curves = curves ?? throw new ArgumentNullException(nameof(curves)); /// /// Gets an array of one dimensional curves @@ -31,16 +28,13 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { if (base.Equals(other) && other is IccCurveSetProcessElement element) { - return this.Curves.SequenceEqual(element.Curves); + return this.Curves.AsSpan().SequenceEqual(element.Curves); } return false; } /// - public bool Equals(IccCurveSetProcessElement other) - { - return this.Equals((IccMultiProcessElement)other); - } + public bool Equals(IccCurveSetProcessElement other) => this.Equals((IccMultiProcessElement)other); } } \ No newline at end of file diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccChromaticityTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccChromaticityTagDataEntry.cs index b4d711f59e..813271d48b 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccChromaticityTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccChromaticityTagDataEntry.cs @@ -80,10 +80,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc public double[][] ChannelValues { get; } /// - public override bool Equals(IccTagDataEntry other) - { - return other is IccChromaticityTagDataEntry entry && this.Equals(entry); - } + public override bool Equals(IccTagDataEntry other) => other is IccChromaticityTagDataEntry entry && this.Equals(entry); /// public bool Equals(IccChromaticityTagDataEntry other) @@ -102,10 +99,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc } /// - public override bool Equals(object obj) - { - return obj is IccChromaticityTagDataEntry other && this.Equals(other); - } + public override bool Equals(object obj) => obj is IccChromaticityTagDataEntry other && this.Equals(other); /// public override int GetHashCode() @@ -162,7 +156,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc for (int i = 0; i < this.ChannelValues.Length; i++) { - if (!this.ChannelValues[i].SequenceEqual(entry.ChannelValues[i])) + if (!this.ChannelValues[i].AsSpan().SequenceEqual(entry.ChannelValues[i])) { return false; } diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantTableTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantTableTagDataEntry.cs index 28d01450c1..aff33ff828 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantTableTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantTableTagDataEntry.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Linq; namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { @@ -42,10 +41,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc public IccColorantTableEntry[] ColorantData { get; } /// - public override bool Equals(IccTagDataEntry other) - { - return other is IccColorantTableTagDataEntry entry && this.Equals(entry); - } + public override bool Equals(IccTagDataEntry other) => other is IccColorantTableTagDataEntry entry && this.Equals(entry); /// public bool Equals(IccColorantTableTagDataEntry other) @@ -60,19 +56,13 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc return true; } - return base.Equals(other) && this.ColorantData.SequenceEqual(other.ColorantData); + return base.Equals(other) && this.ColorantData.AsSpan().SequenceEqual(other.ColorantData); } /// - public override bool Equals(object obj) - { - return obj is IccColorantTableTagDataEntry other && this.Equals(other); - } + public override bool Equals(object obj) => obj is IccColorantTableTagDataEntry other && this.Equals(other); /// - public override int GetHashCode() - { - return HashCode.Combine(this.Signature, this.ColorantData); - } + public override int GetHashCode() => HashCode.Combine(this.Signature, this.ColorantData); } } \ No newline at end of file diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut16TagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut16TagDataEntry.cs index d84fc1431e..8f7db49ad0 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut16TagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut16TagDataEntry.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Linq; using System.Numerics; namespace SixLabors.ImageSharp.Metadata.Profiles.Icc @@ -71,7 +70,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc bool is3By3 = matrix.GetLength(0) == 3 && matrix.GetLength(1) == 3; Guard.IsTrue(is3By3, nameof(matrix), "Matrix must have a size of three by three"); - this.Matrix = this.CreateMatrix(matrix); + this.Matrix = CreateMatrix(matrix); this.InputValues = inputValues ?? throw new ArgumentNullException(nameof(inputValues)); this.ClutValues = clutValues ?? throw new ArgumentNullException(nameof(clutValues)); this.OutputValues = outputValues ?? throw new ArgumentNullException(nameof(outputValues)); @@ -111,10 +110,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc public IccLut[] OutputValues { get; } /// - public override bool Equals(IccTagDataEntry other) - { - return other is IccLut16TagDataEntry entry && this.Equals(entry); - } + public override bool Equals(IccTagDataEntry other) => other is IccLut16TagDataEntry entry && this.Equals(entry); /// public bool Equals(IccLut16TagDataEntry other) @@ -131,16 +127,13 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc return base.Equals(other) && this.Matrix.Equals(other.Matrix) - && this.InputValues.SequenceEqual(other.InputValues) + && this.InputValues.AsSpan().SequenceEqual(other.InputValues) && this.ClutValues.Equals(other.ClutValues) - && this.OutputValues.SequenceEqual(other.OutputValues); + && this.OutputValues.AsSpan().SequenceEqual(other.OutputValues); } /// - public override bool Equals(object obj) - { - return obj is IccLut16TagDataEntry other && this.Equals(other); - } + public override bool Equals(object obj) => obj is IccLut16TagDataEntry other && this.Equals(other); /// public override int GetHashCode() @@ -153,7 +146,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc this.OutputValues); } - private Matrix4x4 CreateMatrix(float[,] matrix) + private static Matrix4x4 CreateMatrix(float[,] matrix) { return new Matrix4x4( matrix[0, 0], diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut8TagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut8TagDataEntry.cs index 5c8ce2d2c9..3a1859a1c3 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut8TagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut8TagDataEntry.cs @@ -114,10 +114,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc public IccLut[] OutputValues { get; } /// - public override bool Equals(IccTagDataEntry other) - { - return other is IccLut8TagDataEntry entry && this.Equals(entry); - } + public override bool Equals(IccTagDataEntry other) => other is IccLut8TagDataEntry entry && this.Equals(entry); /// public bool Equals(IccLut8TagDataEntry other) @@ -134,16 +131,13 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc return base.Equals(other) && this.Matrix.Equals(other.Matrix) - && this.InputValues.SequenceEqual(other.InputValues) + && this.InputValues.AsSpan().SequenceEqual(other.InputValues) && this.ClutValues.Equals(other.ClutValues) - && this.OutputValues.SequenceEqual(other.OutputValues); + && this.OutputValues.AsSpan().SequenceEqual(other.OutputValues); } /// - public override bool Equals(object obj) - { - return obj is IccLut8TagDataEntry other && this.Equals(other); - } + public override bool Equals(object obj) => obj is IccLut8TagDataEntry other && this.Equals(other); /// public override int GetHashCode() diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutAToBTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutAToBTagDataEntry.cs index 4cee555e51..88e3c4cae2 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutAToBTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutAToBTagDataEntry.cs @@ -5,6 +5,7 @@ using System; using System.Linq; using System.Numerics; +// TODO: Review the use of base IccTagDataEntry comparison. namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// @@ -15,12 +16,12 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc /// /// Initializes a new instance of the class. /// - /// A Curve - /// CLUT - /// M Curve + /// B Curve /// Two dimensional conversion matrix (3x3) /// One dimensional conversion matrix (3x1) - /// B Curve + /// M Curve + /// CLUT + /// A Curve public IccLutAToBTagDataEntry( IccTagDataEntry[] curveB, float[,] matrix3x3, @@ -35,12 +36,12 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc /// /// Initializes a new instance of the class. /// - /// A Curve - /// CLUT - /// M Curve + /// B Curve /// Two dimensional conversion matrix (3x3) /// One dimensional conversion matrix (3x1) - /// B Curve + /// M Curve + /// CLUT + /// A Curve /// Tag Signature public IccLutAToBTagDataEntry( IccTagDataEntry[] curveB, @@ -145,10 +146,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc public IccTagDataEntry[] CurveA { get; } /// - public override bool Equals(IccTagDataEntry other) - { - return other is IccLutAToBTagDataEntry entry && this.Equals(entry); - } + public override bool Equals(IccTagDataEntry other) => other is IccLutAToBTagDataEntry entry && this.Equals(entry); /// public bool Equals(IccLutAToBTagDataEntry other) @@ -169,23 +167,18 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc && this.Matrix3x3.Equals(other.Matrix3x3) && this.Matrix3x1.Equals(other.Matrix3x1) && this.ClutValues.Equals(other.ClutValues) - && this.EqualsCurve(this.CurveB, other.CurveB) - && this.EqualsCurve(this.CurveM, other.CurveM) - && this.EqualsCurve(this.CurveA, other.CurveA); + && EqualsCurve(this.CurveB, other.CurveB) + && EqualsCurve(this.CurveM, other.CurveM) + && EqualsCurve(this.CurveA, other.CurveA); } /// - public override bool Equals(object obj) - { - return obj is IccLutAToBTagDataEntry other && this.Equals(other); - } + public override bool Equals(object obj) => obj is IccLutAToBTagDataEntry other && this.Equals(other); /// public override int GetHashCode() { -#pragma warning disable SA1129 // Do not use default value type constructor - var hashCode = new HashCode(); -#pragma warning restore SA1129 // Do not use default value type constructor + HashCode hashCode = default; hashCode.Add(this.Signature); hashCode.Add(this.InputChannelCount); @@ -200,7 +193,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc return hashCode.ToHashCode(); } - private bool EqualsCurve(IccTagDataEntry[] thisCurves, IccTagDataEntry[] entryCurves) + private static bool EqualsCurve(IccTagDataEntry[] thisCurves, IccTagDataEntry[] entryCurves) { bool thisNull = thisCurves is null; bool entryNull = entryCurves is null; @@ -243,10 +236,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc && this.CurveA != null; } - private bool IsB() - { - return this.CurveB != null; - } + private bool IsB() => this.CurveB != null; private void VerifyCurve(IccTagDataEntry[] curves, string name) { diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutBToATagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutBToATagDataEntry.cs index 8e9479ff83..f8bf3f0423 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutBToATagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutBToATagDataEntry.cs @@ -5,6 +5,7 @@ using System; using System.Linq; using System.Numerics; +// TODO: Review the use of base IccTagDataEntry comparison. namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// @@ -15,12 +16,12 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc /// /// Initializes a new instance of the class. /// - /// A Curve - /// CLUT - /// M Curve + /// B Curve /// Two dimensional conversion matrix (3x3) /// One dimensional conversion matrix (3x1) - /// B Curve + /// M Curve + /// CLUT + /// A Curve public IccLutBToATagDataEntry( IccTagDataEntry[] curveB, float[,] matrix3x3, @@ -35,12 +36,12 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc /// /// Initializes a new instance of the class. /// - /// A Curve - /// CLUT - /// M Curve + /// B Curve /// Two dimensional conversion matrix (3x3) /// One dimensional conversion matrix (3x1) - /// B Curve + /// M Curve + /// CLUT + /// A Curve /// Tag Signature public IccLutBToATagDataEntry( IccTagDataEntry[] curveB, @@ -145,10 +146,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc public IccTagDataEntry[] CurveA { get; } /// - public override bool Equals(IccTagDataEntry other) - { - return other is IccLutBToATagDataEntry entry && this.Equals(entry); - } + public override bool Equals(IccTagDataEntry other) => other is IccLutBToATagDataEntry entry && this.Equals(entry); /// public bool Equals(IccLutBToATagDataEntry other) @@ -175,18 +173,12 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc } /// - public override bool Equals(object obj) - { - return obj is IccLutBToATagDataEntry other && this.Equals(other); - } + public override bool Equals(object obj) => obj is IccLutBToATagDataEntry other && this.Equals(other); /// public override int GetHashCode() { -#pragma warning disable SA1129 // Do not use default value type constructor - var hashCode = new HashCode(); -#pragma warning restore SA1129 // Do not use default value type constructor - + HashCode hashCode = default; hashCode.Add(this.Signature); hashCode.Add(this.InputChannelCount); hashCode.Add(this.OutputChannelCount); @@ -243,10 +235,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc && this.CurveA != null; } - private bool IsB() - { - return this.CurveB != null; - } + private bool IsB() => this.CurveB != null; private void VerifyCurve(IccTagDataEntry[] curves, string name) { diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiLocalizedUnicodeTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiLocalizedUnicodeTagDataEntry.cs index 3084ff6121..34a027126f 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiLocalizedUnicodeTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiLocalizedUnicodeTagDataEntry.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Linq; namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { @@ -56,7 +55,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc return true; } - return base.Equals(other) && this.Texts.SequenceEqual(other.Texts); + return base.Equals(other) && this.Texts.AsSpan().SequenceEqual(other.Texts); } /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiProcessElementsTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiProcessElementsTagDataEntry.cs index dc78e75200..f2713e8cee 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiProcessElementsTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiProcessElementsTagDataEntry.cs @@ -57,10 +57,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc /// public override bool Equals(IccTagDataEntry other) - { - var entry = other as IccMultiProcessElementsTagDataEntry; - return entry != null && this.Equals(entry); - } + => other is IccMultiProcessElementsTagDataEntry entry && this.Equals(entry); /// public bool Equals(IccMultiProcessElementsTagDataEntry other) @@ -78,14 +75,11 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc return base.Equals(other) && this.InputChannelCount == other.InputChannelCount && this.OutputChannelCount == other.OutputChannelCount - && this.Data.SequenceEqual(other.Data); + && this.Data.AsSpan().SequenceEqual(other.Data); } /// - public override bool Equals(object obj) - { - return obj is IccMultiProcessElementsTagDataEntry other && this.Equals(other); - } + public override bool Equals(object obj) => obj is IccMultiProcessElementsTagDataEntry other && this.Equals(other); /// public override int GetHashCode() diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccNamedColor2TagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccNamedColor2TagDataEntry.cs index 30516578fe..4fd0cfcfb6 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccNamedColor2TagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccNamedColor2TagDataEntry.cs @@ -143,7 +143,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc && string.Equals(this.Prefix, other.Prefix) && string.Equals(this.Suffix, other.Suffix) && this.VendorFlags == other.VendorFlags - && this.Colors.SequenceEqual(other.Colors); + && this.Colors.AsSpan().SequenceEqual(other.Colors); } /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceDescTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceDescTagDataEntry.cs index 3c3a6c7619..7897c394f1 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceDescTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceDescTagDataEntry.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Linq; namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { @@ -29,9 +28,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc /// Tag Signature public IccProfileSequenceDescTagDataEntry(IccProfileDescription[] descriptions, IccProfileTag tagSignature) : base(IccTypeSignature.ProfileSequenceDesc, tagSignature) - { - this.Descriptions = descriptions ?? throw new ArgumentNullException(nameof(descriptions)); - } + => this.Descriptions = descriptions ?? throw new ArgumentNullException(nameof(descriptions)); /// /// Gets the profile descriptions @@ -40,9 +37,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc /// public override bool Equals(IccTagDataEntry other) - { - return other is IccProfileSequenceDescTagDataEntry entry && this.Equals(entry); - } + => other is IccProfileSequenceDescTagDataEntry entry && this.Equals(entry); /// public bool Equals(IccProfileSequenceDescTagDataEntry other) @@ -57,14 +52,12 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc return true; } - return base.Equals(other) && this.Descriptions.SequenceEqual(other.Descriptions); + return base.Equals(other) && this.Descriptions.AsSpan().SequenceEqual(other.Descriptions); } /// public override bool Equals(object obj) - { - return obj is IccProfileSequenceDescTagDataEntry other && this.Equals(other); - } + => obj is IccProfileSequenceDescTagDataEntry other && this.Equals(other); /// public override int GetHashCode() => HashCode.Combine(this.Signature, this.Descriptions); diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccResponseCurveSet16TagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccResponseCurveSet16TagDataEntry.cs index 6051bf8414..e9c8af9be2 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccResponseCurveSet16TagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccResponseCurveSet16TagDataEntry.cs @@ -51,10 +51,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc public IccResponseCurve[] Curves { get; } /// - public override bool Equals(IccTagDataEntry other) - { - return other is IccResponseCurveSet16TagDataEntry entry && this.Equals(entry); - } + public override bool Equals(IccTagDataEntry other) => other is IccResponseCurveSet16TagDataEntry entry && this.Equals(entry); /// public bool Equals(IccResponseCurveSet16TagDataEntry other) @@ -71,14 +68,11 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc return base.Equals(other) && this.ChannelCount == other.ChannelCount - && this.Curves.SequenceEqual(other.Curves); + && this.Curves.AsSpan().SequenceEqual(other.Curves); } /// - public override bool Equals(object obj) - { - return obj is IccResponseCurveSet16TagDataEntry other && this.Equals(other); - } + public override bool Equals(object obj) => obj is IccResponseCurveSet16TagDataEntry other && this.Equals(other); /// public override int GetHashCode() diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt64ArrayTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt64ArrayTagDataEntry.cs index 053181a26f..09bec9b7a5 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt64ArrayTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt64ArrayTagDataEntry.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Linq; namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { @@ -26,10 +25,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc /// The array data /// Tag Signature public IccUInt64ArrayTagDataEntry(ulong[] data, IccProfileTag tagSignature) - : base(IccTypeSignature.UInt64Array, tagSignature) - { - this.Data = data ?? throw new ArgumentNullException(nameof(data)); - } + : base(IccTypeSignature.UInt64Array, tagSignature) => this.Data = data ?? throw new ArgumentNullException(nameof(data)); /// /// Gets the array data @@ -37,10 +33,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc public ulong[] Data { get; } /// - public override bool Equals(IccTagDataEntry other) - { - return other is IccUInt64ArrayTagDataEntry entry && this.Equals(entry); - } + public override bool Equals(IccTagDataEntry other) => other is IccUInt64ArrayTagDataEntry entry && this.Equals(entry); /// public bool Equals(IccUInt64ArrayTagDataEntry other) @@ -59,10 +52,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc } /// - public override bool Equals(object obj) - { - return obj is IccUInt64ArrayTagDataEntry other && this.Equals(other); - } + public override bool Equals(object obj) => obj is IccUInt64ArrayTagDataEntry other && this.Equals(other); /// public override int GetHashCode() => HashCode.Combine(this.Signature, this.Data); diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccClut.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccClut.cs index c53ba95201..bd8f784ae7 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccClut.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccClut.cs @@ -134,10 +134,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc } /// - public override bool Equals(object obj) - { - return obj is IccClut other && this.Equals(other); - } + public override bool Equals(object obj) => obj is IccClut other && this.Equals(other); /// public override int GetHashCode() diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccNamedColor.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccNamedColor.cs index 22bf1bb762..3c640ab036 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccNamedColor.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccNamedColor.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Linq; namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { @@ -55,10 +54,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc /// /// True if the parameter is equal to the parameter; otherwise, false. /// - public static bool operator ==(IccNamedColor left, IccNamedColor right) - { - return left.Equals(right); - } + public static bool operator ==(IccNamedColor left, IccNamedColor right) => left.Equals(right); /// /// Compares two objects for equality. @@ -68,23 +64,17 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc /// /// True if the parameter is not equal to the parameter; otherwise, false. /// - public static bool operator !=(IccNamedColor left, IccNamedColor right) - { - return !left.Equals(right); - } + public static bool operator !=(IccNamedColor left, IccNamedColor right) => !left.Equals(right); /// - public override bool Equals(object obj) - { - return obj is IccNamedColor other && this.Equals(other); - } + public override bool Equals(object obj) => obj is IccNamedColor other && this.Equals(other); /// public bool Equals(IccNamedColor other) { return this.Name.Equals(other.Name) - && this.PcsCoordinates.SequenceEqual(other.PcsCoordinates) - && this.DeviceCoordinates.SequenceEqual(other.DeviceCoordinates); + && this.PcsCoordinates.AsSpan().SequenceEqual(other.PcsCoordinates) + && this.DeviceCoordinates.AsSpan().SequenceEqual(other.DeviceCoordinates); } /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileDescription.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileDescription.cs index 76ac5961d9..d630f015e9 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileDescription.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileDescription.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Linq; namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { @@ -68,18 +67,15 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc /// public bool Equals(IccProfileDescription other) => - this.DeviceManufacturer == other.DeviceManufacturer && - this.DeviceModel == other.DeviceModel && - this.DeviceAttributes == other.DeviceAttributes && - this.TechnologyInformation == other.TechnologyInformation && - this.DeviceManufacturerInfo.SequenceEqual(other.DeviceManufacturerInfo) && - this.DeviceModelInfo.SequenceEqual(other.DeviceModelInfo); + this.DeviceManufacturer == other.DeviceManufacturer + && this.DeviceModel == other.DeviceModel + && this.DeviceAttributes == other.DeviceAttributes + && this.TechnologyInformation == other.TechnologyInformation + && this.DeviceManufacturerInfo.AsSpan().SequenceEqual(other.DeviceManufacturerInfo) + && this.DeviceModelInfo.AsSpan().SequenceEqual(other.DeviceModelInfo); /// - public override bool Equals(object obj) - { - return obj is IccProfileDescription other && this.Equals(other); - } + public override bool Equals(object obj) => obj is IccProfileDescription other && this.Equals(other); /// public override int GetHashCode() diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileSequenceIdentifier.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileSequenceIdentifier.cs index 9eb9fc7c3e..6bf420b49e 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileSequenceIdentifier.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileSequenceIdentifier.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Linq; namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { @@ -34,14 +33,11 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc /// public bool Equals(IccProfileSequenceIdentifier other) => - this.Id.Equals(other.Id) && - this.Description.SequenceEqual(other.Description); + this.Id.Equals(other.Id) + && this.Description.AsSpan().SequenceEqual(other.Description); /// - public override bool Equals(object obj) - { - return obj is IccProfileSequenceIdentifier other && this.Equals(other); - } + public override bool Equals(object obj) => obj is IccProfileSequenceIdentifier other && this.Equals(other); /// public override int GetHashCode() => HashCode.Combine(this.Id, this.Description); diff --git a/src/ImageSharp/Processing/ResizeOptions.cs b/src/ImageSharp/Processing/ResizeOptions.cs index 0d5bfe38bc..ee0dde6b27 100644 --- a/src/ImageSharp/Processing/ResizeOptions.cs +++ b/src/ImageSharp/Processing/ResizeOptions.cs @@ -1,8 +1,8 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; using System.Collections.Generic; -using System.Linq; using SixLabors.ImageSharp.Processing.Processors.Transforms; using SixLabors.Primitives; @@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp.Processing /// /// Gets or sets the center coordinates. /// - public IEnumerable CenterCoordinates { get; set; } = Enumerable.Empty(); + public IEnumerable CenterCoordinates { get; set; } = Array.Empty(); /// /// Gets or sets the target size. From c5b14312b0e13da5bc2e57ee96213823be92f638 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Sat, 1 Jun 2019 05:14:03 -0700 Subject: [PATCH 211/223] Updating the repo to use Directory.Build.props/targets files (#920) * Updating the repo to use Directory.Build.props/targets files * Adding an InternalsVisibleTo for DynamicProxyGenAssembly2, PublicKeyToken=null * Removing duplicate includes from the ImageSharp.csproj * Updating the .gitattributes file to explicitly list the line endings * Removing the ImageSharp.ruleset file, as the one from standards should be used instead * Updating the package version management to use `PackageReference Update` --- .editorconfig | 369 +++++++++++++++++- .gitattributes | 124 +++--- Directory.Build.props | 77 ++++ Directory.Build.targets | 47 +++ ImageSharp.ruleset | 15 - ImageSharp.sln | 361 ++++++++++++++++- NuGet.config | 9 - src/Directory.Build.props | 41 ++ src/Directory.Build.targets | 55 +++ .../ImageSharp.Drawing.csproj | 50 +-- .../Properties/AssemblyInfo.cs | 4 - src/ImageSharp/ImageSharp.csproj | 204 +++++----- src/ImageSharp/Properties/AssemblyInfo.cs | 5 +- src/Shared/AssemblyInfo.Common.cs | 15 - tests/Directory.Build.props | 30 ++ tests/Directory.Build.targets | 19 + .../ImageSharp.Benchmarks.csproj | 36 +- .../ImageSharp.Sandbox46.csproj | 36 +- .../Properties/AssemblyInfo.cs | 15 - .../ImageSharp.Tests/ImageSharp.Tests.csproj | 57 +-- 20 files changed, 1209 insertions(+), 360 deletions(-) create mode 100644 Directory.Build.props create mode 100644 Directory.Build.targets delete mode 100644 ImageSharp.ruleset delete mode 100644 NuGet.config create mode 100644 src/Directory.Build.props create mode 100644 src/Directory.Build.targets delete mode 100644 src/ImageSharp.Drawing/Properties/AssemblyInfo.cs delete mode 100644 src/Shared/AssemblyInfo.Common.cs create mode 100644 tests/Directory.Build.props create mode 100644 tests/Directory.Build.targets delete mode 100644 tests/ImageSharp.Sandbox46/Properties/AssemblyInfo.cs diff --git a/.editorconfig b/.editorconfig index b725c5cce3..8f0e28eec6 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,20 +1,371 @@ -# top-most EditorConfig file +############################################################################### +# EditorConfig is awesome: http://EditorConfig.org +############################################################################### + +############################################################################### +# Top-most EditorConfig file +############################################################################### root = true -[*.cs] +############################################################################### +# Set default behavior to: +# a UTF-8 encoding, +# Unix-style line endings, +# a newline ending the file, +# 4 space indentation, and +# trimming of trailing whitespace +############################################################################### +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true indent_style = space indent_size = 4 -csharp_style_var_for_built_in_types = false:warning -csharp_style_var_elsewhere = false:warning -csharp_style_var_when_type_is_apparent = true:warning +trim_trailing_whitespace = true + +############################################################################### +# Set file behavior to: +# 2 space indentation +############################################################################### +[*.{cmd,config,csproj,json,props,ps1,resx,sh,targets}] +indent_size = 2 + +############################################################################### +# Set file behavior to: +# Windows-style line endings, and +# tabular indentation +############################################################################### +[*.sln] end_of_line = crlf +indent_style = tab + +############################################################################### +# Set dotnet naming rules to: +# suggest async members be pascal case suffixed with Async +# suggest const declarations be pascal case +# suggest interfaces be pascal case prefixed with I +# suggest parameters be camel case +# suggest private and internal static fields be camel case +# suggest private and internal fields be camel case +# suggest public and protected declarations be pascal case +# suggest static readonly declarations be pascal case +# suggest type parameters be prefixed with T +############################################################################### +[*.cs] +dotnet_naming_rule.async_members_should_be_pascal_case_suffixed_with_async.severity = suggestion +dotnet_naming_rule.async_members_should_be_pascal_case_suffixed_with_async.style = pascal_case_suffixed_with_async +dotnet_naming_rule.async_members_should_be_pascal_case_suffixed_with_async.symbols = async_members + +dotnet_naming_rule.const_declarations_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.const_declarations_should_be_pascal_case.style = pascal_case +dotnet_naming_rule.const_declarations_should_be_pascal_case.symbols = const_declarations + +dotnet_naming_rule.interfaces_should_be_pascal_case_prefixed_with_i.severity = suggestion +dotnet_naming_rule.interfaces_should_be_pascal_case_prefixed_with_i.style = pascal_case_prefixed_with_i +dotnet_naming_rule.interfaces_should_be_pascal_case_prefixed_with_i.symbols = interfaces + +dotnet_naming_rule.parameters_should_be_camel_case.severity = suggestion +dotnet_naming_rule.parameters_should_be_camel_case.style = camel_case +dotnet_naming_rule.parameters_should_be_camel_case.symbols = parameters + +dotnet_naming_rule.private_and_internal_static_fields_should_be_camel_case.severity = suggestion +dotnet_naming_rule.private_and_internal_static_fields_should_be_camel_case.style = camel_case +dotnet_naming_rule.private_and_internal_static_fields_should_be_camel_case.symbols = private_and_internal_static_fields + +dotnet_naming_rule.private_and_internal_fields_should_be_camel_case.severity = suggestion +dotnet_naming_rule.private_and_internal_fields_should_be_camel_case.style = camel_case +dotnet_naming_rule.private_and_internal_fields_should_be_camel_case.symbols = private_and_internal_fields + +dotnet_naming_rule.public_and_protected_declarations_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.public_and_protected_declarations_should_be_pascal_case.style = pascal_case +dotnet_naming_rule.public_and_protected_declarations_should_be_pascal_case.symbols = public_and_protected_declarations + +dotnet_naming_rule.static_readonly_declarations_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.static_readonly_declarations_should_be_pascal_case.style = pascal_case +dotnet_naming_rule.static_readonly_declarations_should_be_pascal_case.symbols = static_readonly_declarations + +dotnet_naming_rule.type_parameters_should_be_pascal_case_prefixed_with_t.severity = suggestion +dotnet_naming_rule.type_parameters_should_be_pascal_case_prefixed_with_t.style = pascal_case_prefixed_with_t +dotnet_naming_rule.type_parameters_should_be_pascal_case_prefixed_with_t.symbols = type_parameters + +############################################################################### +# Set dotnet naming styles to define: +# camel case +# pascal case +# pascal case suffixed with Async +# pascal case prefixed with I +# pascal case prefixed with T +############################################################################### +[*.cs] +dotnet_naming_style.camel_case.capitalization = camel_case + +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.pascal_case_suffixed_with_async.capitalization = pascal_case +dotnet_naming_style.pascal_case_suffixed_with_async.required_suffix = Async + +dotnet_naming_style.pascal_case_prefixed_with_i.capitalization = pascal_case +dotnet_naming_style.pascal_case_prefixed_with_i.required_prefix = I + +dotnet_naming_style.pascal_case_prefixed_with_t.capitalization = pascal_case +dotnet_naming_style.pascal_case_prefixed_with_t.required_prefix = T + +############################################################################### +# Set dotnet naming symbols to: +# async members +# const declarations +# interfaces +# private and internal fields +# private and internal static fields +# public and protected declarations +# static readonly declarations +# type parameters +############################################################################### +[*.cs] +dotnet_naming_symbols.async_members.required_modifiers = async + +dotnet_naming_symbols.const_declarations.required_modifiers = const + +dotnet_naming_symbols.interfaces.applicable_kinds = interface + +dotnet_naming_symbols.parameters.applicable_kinds = parameter + +dotnet_naming_symbols.private_and_internal_fields.applicable_accessibilities = private, internal +dotnet_naming_symbols.private_and_internal_fields.applicable_kinds = field + +dotnet_naming_symbols.private_and_internal_static_fields.applicable_accessibilities = private, internal +dotnet_naming_symbols.private_and_internal_static_fields.applicable_kinds = field +dotnet_naming_symbols.private_and_internal_static_fields.required_modifiers = static + +dotnet_naming_symbols.public_and_protected_declarations.applicable_accessibilities = public, protected + +dotnet_naming_symbols.static_readonly_declarations.required_modifiers = static, readonly + +dotnet_naming_symbols.type_parameters.applicable_kinds = type_parameter + +############################################################################### +# Set dotnet sort options to: +# do not separate import directives into groups, and +# sort system directives first +############################################################################### +[*.cs] +dotnet_separate_import_directive_groups = false dotnet_sort_system_directives_first = true + +############################################################################### +# Set dotnet style options to: +# suggest null-coalescing expressions, +# suggest collection-initializers, +# suggest explicit tuple names, +# suggest null-propogation +# suggest object-initializers, +# generate parentheses in arithmetic binary operators for clarity, +# generate parentheses in other binary operators for clarity, +# don't generate parentheses in other operators if unnecessary, +# generate parentheses in relational binary operators for clarity, +# warn when not using predefined-types for locals, parameters, and members, +# generate predefined-types of type names for member access, +# generate auto properties, +# suggest compound assignment, +# generate conditional expression over assignment, +# generate conditional expression over return, +# suggest inferred anonymous types, +# suggest inferred tuple names, +# suggest 'is null' checks over '== null', +# don't generate 'this.' and 'Me.' for events, +# warn when not using 'this.' and 'Me.' for fields, +# warn when not using 'this.' and 'Me.' for methods, +# warn when not using 'this.' and 'Me.' for properties, +# suggest readonly fields, and +# generate accessibility modifiers for non interface members +############################################################################### +[*.cs] +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_object_initializer = true:suggestion + +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent + dotnet_style_predefined_type_for_locals_parameters_members = true:warning -dotnet_style_predefined_type_for_member_access = true:warning +dotnet_style_predefined_type_for_member_access = true:silent + +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion + +dotnet_style_qualification_for_event = false:silent dotnet_style_qualification_for_field = true:warning dotnet_style_qualification_for_method = true:warning dotnet_style_qualification_for_property = true:warning -[*.tt] -indent_style = space -indent_size = 4 \ No newline at end of file +dotnet_style_readonly_field = true:suggestion +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent + +############################################################################### +# Set dotnet style options to: +# suggest removing all unused parameters +############################################################################### +[*.cs] +dotnet_code_quality_unused_parameters = all:suggestion + +############################################################################### +# Set csharp indent options to: +# indent block contents, +# not indent braces, +# indent case contents, +# not indent case contents when block, +# indent labels one less than the current, and +# indent switch labels +############################################################################### +[*.cs] +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = false +csharp_indent_labels = one_less_than_current +csharp_indent_switch_labels = true + +############################################################################### +# Set csharp new-line options to: +# insert a new-line before "catch", +# insert a new-line before "else", +# insert a new-line before "finally", +# insert a new-line before members in anonymous-types, +# insert a new-line before members in object-initializers, and +# insert a new-line before all open braces +############################################################################### +[*.cs] +csharp_new_line_before_catch = true +csharp_new_line_before_else = true +csharp_new_line_before_finally = true + +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_members_in_object_initializers = true + +csharp_new_line_before_open_brace = all + +############################################################################### +# Set csharp preserve options to: +# preserve single-line blocks, and +# preserve single-line statements +############################################################################### +[*.cs] +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true + +############################################################################### +# Set csharp space options to: +# remove any space after a cast, +# add a space after the colon in an inheritance clause, +# add a space after a comma, +# remove any space after a dot, +# add a space after keywords in control flow statements, +# add a space after a semicolon in a "for" statement, +# add a space before and after binary operators, +# remove space around declaration statements, +# add a space before the colon in an inheritance clause, +# remove any space before a comma, +# remove any space before a dot, +# remove any space before an open square-bracket, +# remove any space before a semicolon in a "for" statement, +# remove any space between empty square-brackets, +# remove any space between a method call's empty parameter list parenthesis, +# remove any space between a method call's name and its opening parenthesis, +# remove any space between a method call's parameter list parenthesis, +# remove any space between a method declaration's empty parameter list parenthesis, +# remove any space between a method declaration's name and its openening parenthesis, +# remove any space between a method declaration's parameter list parenthesis, +# remove any space between parentheses, and +# remove any space between square brackets +############################################################################### +[*.cs] +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true + +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = do_not_ignore + +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false + +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +############################################################################### +# Set csharp style options to: +# generate braces, +# suggest simple default expressions, +# generate a preferred modifier order, +# suggest conditional delegate calls, +# suggest deconstructed variable declarations, +# generate expression-bodied accessors, +# don't generate expression-bodied constructors, +# generate expression-bodied indexers, +# generate expression-bodied lambdas, +# don't generate expression-bodied methods, +# don't generate expression-bodied operators, +# generate expression-bodied properties, +# suggest inlined variable declarations, +# suggest local over anonymous functions, +# suggest pattern-matching over "as" with "null" check, +# suggest pattern-matching over "is" with "cast" check, +# suggest throw expressions, +# generate a discard variable for unused value expression statements, +# suggest a discard variable for unused assignments, +# warn when using var for built-in types, +# warn when using var when the type is not apparent, and +# warn when not using var when the type is apparent +############################################################################### +[*.cs] +csharp_prefer_braces = true:silent +csharp_prefer_simple_default_expression = true:suggestion +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent + +csharp_style_conditional_delegate_call = true:suggestion +csharp_style_deconstructed_variable_declaration = true:suggestion + +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent + +csharp_style_inlined_variable_declaration = true:suggestion + +csharp_style_pattern_local_over_anonymous_function = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion + +csharp_style_throw_expression = true:suggestion + +csharp_style_unused_value_expression_statement_preference = discard_variable:silent +csharp_style_unused_value_assignment_preference = discard_variable:suggestion + +csharp_style_var_for_built_in_types = false:warning +csharp_style_var_elsewhere = false:warning +csharp_style_var_when_type_is_apparent = true:warning diff --git a/.gitattributes b/.gitattributes index 2a7d04dc7b..704a6aa436 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,50 +1,82 @@ -*.doc diff=astextplain -*.DOC diff=astextplain -*.docx diff=astextplain -*.DOCX diff=astextplain -*.dot diff=astextplain -*.DOT diff=astextplain -*.pdf diff=astextplain -*.PDF diff=astextplain -*.rtf diff=astextplain -*.RTF diff=astextplain +############################################################################### +# Set default behavior to: +# treat as text and +# normalize to Unix-style line endings +* text eol=lf -*.jpg binary -*.png binary -*.gif binary -*.cs text=auto diff=csharp -*.vb text=auto -*.c text=auto -*.cpp text=auto -*.cxx text=auto -*.h text=auto -*.hxx text=auto -*.py text=auto -*.rb text=auto -*.java text=auto -*.html text=auto -*.htm text=auto -*.css text=auto -*.scss text=auto -*.sass text=auto -*.less text=auto -*.js text=auto -*.lisp text=auto -*.clj text=auto -*.sql text=auto -*.php text=auto -*.lua text=auto -*.m text=auto -*.asm text=auto -*.erl text=auto -*.fs text=auto -*.fsx text=auto -*.hs text=auto -*.csproj text=auto merge=union -*.vbproj text=auto merge=union -*.fsproj text=auto merge=union -*.dbproj text=auto merge=union -*.sln text=auto eol=crlf merge=union +# Set explicit file behavior to: +*.asm text eol=lf +*.c text eol=lf +*.clj text eol=lf +*.cmd text eol=lf +*.cpp text eol=lf +*.css text eol=lf +*.cxx text eol=lf +*.config text eol=lf +*.DotSettings text eol=lf +*.erl text eol=lf +*.fs text eol=lf +*.fsx text eol=lf +*.h text eol=lf +*.htm text eol=lf +*.html text eol=lf +*.hs text eol=lf +*.hxx text eol=lf +*.java text eol=lf +*.js text eol=lf +*.json text eol=lf +*.less text eol=lf +*.lisp text eol=lf +*.lua text eol=lf +*.m text eol=lf +*.md text eol=lf +*.php text eol=lf +*.props text eol=lf +*.ps1 text eol=lf +*.py text eol=lf +*.rb text eol=lf +*.resx text eol=lf +*.runsettings text eol=lf +*.ruleset text eol=lf +*.sass text eol=lf +*.scss text eol=lf +*.sh text eol=lf +*.sql text eol=lf +*.svg text eol=lf +*.targets text eol=lf +*.tt text eol=lf +*.ttinclude text eol=lf +*.txt text eol=lf +*.vb text eol=lf +*.yml text eol=lf +# treat as text +# normalize to Unix-style line endings and +# diff as csharp +*.cs text eol=lf diff=csharp +# use a union merge when resoling conflicts +*.csproj text eol=lf merge=union +*.dbproj text eol=lf merge=union +*.fsproj text eol=lf merge=union +*.ncrunchproject text eol=lf merge=union +*.vbproj text eol=lf merge=union +# normalize to Windows-style line endings and +*.sln text eol=crlf merge=union +# treat as binary +*.bmp binary +*.dll binary +*.exe binary +*.gif binary +*.jpg binary +*.png binary +*.ttf binary +*.snk binary +# diff as plain text +*.doc diff=astextplain +*.docx diff=astextplain +*.dot diff=astextplain +*.pdf diff=astextplain +*.pptx diff=astextplain +*.rtf diff=astextplain *.jpg filter=lfs diff=lfs merge=lfs -text *.jpeg filter=lfs diff=lfs merge=lfs -text *.bmp filter=lfs diff=lfs merge=lfs -text diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 0000000000..9c92c14b8a --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,77 @@ + + + + + + + + $(MSBuildThisFileDirectory)artifacts/ + $(ImageSharpProjectCategory)/$(MSBuildProjectName) + https://github.com/SixLabors/ImageSharp/ + + + + + true + $(BaseArtifactsPath)obj/$(BaseArtifactsPathSuffix)/ + portable + full + disable + true + false + true + true + + + + true + + + + + Six Labors and contributors + $(BaseArtifactsPath)bin/$(BaseArtifactsPathSuffix)/ + Six Labors + $(BaseArtifactsPath)pkg/$(BaseArtifactsPathSuffix)/$(Configuration)/ + SixLabors.ImageSharp + 0.0.1 + $(PackageVersion) + + + + + + $(MSBuildThisFileDirectory)standards/SixLabors.snk + Copyright © Six Labors and Contributors + strict;IOperation + true + 7.3 + en + true + https://raw.githubusercontent.com/SixLabors/Branding/master/icons/imagesharp/sixlabors.imagesharp.128.png + Apache-2.0 + $(RepositoryUrl) + true + git + + https://www.myget.org/F/sixlabors/api/v3/index.json; + https://dotnetfeed.blob.core.windows.net/dotnet-core/index.json; + https://api.nuget.org/v3/index.json; + + 002400000c8000009400000006020000002400005253413100040000010001000147e6fe6766715eec6cfed61f1e7dcdbf69748a3e355c67e9d8dfd953acab1d5e012ba34b23308166fdc61ee1d0390d5f36d814a6091dd4b5ed9eda5a26afced924c683b4bfb4b3d64b0586a57eff9f02b1f84e3cb0ddd518bd1697f2c84dcbb97eb8bb5c7801be12112ed0ec86db934b0e9a5171e6bb1384b6d2f7d54dfa97 + true + + + + + + + + diff --git a/Directory.Build.targets b/Directory.Build.targets new file mode 100644 index 0000000000..3504398794 --- /dev/null +++ b/Directory.Build.targets @@ -0,0 +1,47 @@ + + + + + + + + $(DefineConstants);$(OS) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ImageSharp.ruleset b/ImageSharp.ruleset deleted file mode 100644 index d318b75c2e..0000000000 --- a/ImageSharp.ruleset +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/ImageSharp.sln b/ImageSharp.sln index 3c3817bf63..1fd5e2d8b4 100644 --- a/ImageSharp.sln +++ b/ImageSharp.sln @@ -1,47 +1,356 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26730.12 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.28902.138 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{C317F1B1-D75E-4C6D-83EB-80367343E0D7}" ProjectSection(SolutionItems) = preProject - standards\.editorconfig = standards\.editorconfig + .editorconfig = .editorconfig + .gitattributes = .gitattributes + .gitignore = .gitignore + .gitmodules = .gitmodules .travis.yml = .travis.yml appveyor.yml = appveyor.yml - .github\ISSUE_TEMPLATE\ask-question.md = .github\ISSUE_TEMPLATE\ask-question.md - .github\ISSUE_TEMPLATE\bug-report.md = .github\ISSUE_TEMPLATE\bug-report.md + build.cmd = build.cmd + build.ps1 = build.ps1 codecov.yml = codecov.yml CodeCoverage.runsettings = CodeCoverage.runsettings - .github\CONTRIBUTING.md = .github\CONTRIBUTING.md - .github\ISSUE_TEMPLATE\feature-request.md = .github\ISSUE_TEMPLATE\feature-request.md - features.md = features.md + Directory.Build.props = Directory.Build.props + Directory.Build.targets = Directory.Build.targets ImageSharp.sln.DotSettings = ImageSharp.sln.DotSettings - NuGet.config = NuGet.config - .github\PULL_REQUEST_TEMPLATE.md = .github\PULL_REQUEST_TEMPLATE.md + LICENSE = LICENSE README.md = README.md run-tests.ps1 = run-tests.ps1 - standards\SixLabors.ruleset = standards\SixLabors.ruleset - standards\stylecop.json = standards\stylecop.json + stylecop.json = stylecop.json EndProjectSection EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source", "Source", "{815C0625-CD3D-440F-9F80-2D83856AB7AE}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{1799C43E-5C54-4A8F-8D64-B1475241DB0D}" ProjectSection(SolutionItems) = preProject - src\README.md = src\README.md + .github\CONTRIBUTING.md = .github\CONTRIBUTING.md + .github\PULL_REQUEST_TEMPLATE.md = .github\PULL_REQUEST_TEMPLATE.md EndProjectSection EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{56801022-D71A-4FBE-BC5B-CBA08E2284EC}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ISSUE_TEMPLATE", "ISSUE_TEMPLATE", "{FBE8C1AD-5AEC-4514-9B64-091D8E145865}" + ProjectSection(SolutionItems) = preProject + .github\ISSUE_TEMPLATE\ask-question.md = .github\ISSUE_TEMPLATE\ask-question.md + .github\ISSUE_TEMPLATE\bug-report.md = .github\ISSUE_TEMPLATE\bug-report.md + .github\ISSUE_TEMPLATE\feature-request.md = .github\ISSUE_TEMPLATE\feature-request.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".vscode", ".vscode", "{0274D4CF-9932-47CC-8E89-54DC05B8F06E}" + ProjectSection(SolutionItems) = preProject + .vscode\launch.json = .vscode\launch.json + .vscode\tasks.json = .vscode\tasks.json + EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{E919DF0B-2607-4462-8FC0-5C98FE50F8C9}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{9E574A07-F879-4811-9C41-5CBDC6BAFDB7}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "icons", "icons", "{2B02E303-7CC6-4E15-97EE-DBE86B287553}" ProjectSection(SolutionItems) = preProject - src\Shared\AssemblyInfo.Common.cs = src\Shared\AssemblyInfo.Common.cs + build\icons\imagesharp-logo-128.png = build\icons\imagesharp-logo-128.png + build\icons\imagesharp-logo-256.png = build\icons\imagesharp-logo-256.png + build\icons\imagesharp-logo-32.png = build\icons\imagesharp-logo-32.png + build\icons\imagesharp-logo-512.png = build\icons\imagesharp-logo-512.png + build\icons\imagesharp-logo-64.png = build\icons\imagesharp-logo-64.png + build\icons\imagesharp-logo.png = build\icons\imagesharp-logo.png + build\icons\imagesharp-logo.svg = build\icons\imagesharp-logo.svg + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{815C0625-CD3D-440F-9F80-2D83856AB7AE}" + ProjectSection(SolutionItems) = preProject + src\Directory.Build.props = src\Directory.Build.props + src\Directory.Build.targets = src\Directory.Build.targets + src\README.md = src\README.md EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp", "src\ImageSharp\ImageSharp.csproj", "{2AA31A1F-142C-43F4-8687-09ABCA4B3A26}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Drawing", "src\ImageSharp.Drawing\ImageSharp.Drawing.csproj", "{2E33181E-6E28-4662-A801-E2E7DC206029}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{56801022-D71A-4FBE-BC5B-CBA08E2284EC}" + ProjectSection(SolutionItems) = preProject + tests\Directory.Build.props = tests\Directory.Build.props + tests\Directory.Build.targets = tests\Directory.Build.targets + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CodeCoverage", "CodeCoverage", "{D4C5EC58-F8E6-4636-B9EE-C99D2578E5C6}" + ProjectSection(SolutionItems) = preProject + tests\CodeCoverage\CodeCoverage.cmd = tests\CodeCoverage\CodeCoverage.cmd + tests\CodeCoverage\packages.config = tests\CodeCoverage\packages.config + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Images", "Images", "{FA55F5DE-11A6-487D-ABA4-BC93A02717DD}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Input", "Input", "{9DA226A1-8656-49A8-A58A-A8B5C081AD66}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Bmp", "Bmp", "{1A82C5F6-90E0-4E97-BE16-A825C046B493}" + ProjectSection(SolutionItems) = preProject + tests\Images\Input\Bmp\BitmapCoreHeaderQR.bmp = tests\Images\Input\Bmp\BitmapCoreHeaderQR.bmp + tests\Images\Input\Bmp\BITMAPV5HEADER.bmp = tests\Images\Input\Bmp\BITMAPV5HEADER.bmp + tests\Images\Input\Bmp\Car.bmp = tests\Images\Input\Bmp\Car.bmp + tests\Images\Input\Bmp\F.bmp = tests\Images\Input\Bmp\F.bmp + tests\Images\Input\Bmp\issue735.bmp = tests\Images\Input\Bmp\issue735.bmp + tests\Images\Input\Bmp\neg_height.bmp = tests\Images\Input\Bmp\neg_height.bmp + tests\Images\Input\Bmp\pal1.bmp = tests\Images\Input\Bmp\pal1.bmp + tests\Images\Input\Bmp\pal1p1.bmp = tests\Images\Input\Bmp\pal1p1.bmp + tests\Images\Input\Bmp\pal4.bmp = tests\Images\Input\Bmp\pal4.bmp + tests\Images\Input\Bmp\pal4rle.bmp = tests\Images\Input\Bmp\pal4rle.bmp + tests\Images\Input\Bmp\pal8-0.bmp = tests\Images\Input\Bmp\pal8-0.bmp + tests\Images\Input\Bmp\pal8gs.bmp = tests\Images\Input\Bmp\pal8gs.bmp + tests\Images\Input\Bmp\pal8offs.bmp = tests\Images\Input\Bmp\pal8offs.bmp + tests\Images\Input\Bmp\pal8os2sp.bmp = tests\Images\Input\Bmp\pal8os2sp.bmp + tests\Images\Input\Bmp\pal8os2v1_winv2.bmp = tests\Images\Input\Bmp\pal8os2v1_winv2.bmp + tests\Images\Input\Bmp\pal8os2v2-16.bmp = tests\Images\Input\Bmp\pal8os2v2-16.bmp + tests\Images\Input\Bmp\pal8os2v2.bmp = tests\Images\Input\Bmp\pal8os2v2.bmp + tests\Images\Input\Bmp\pal8v4.bmp = tests\Images\Input\Bmp\pal8v4.bmp + tests\Images\Input\Bmp\pal8v5.bmp = tests\Images\Input\Bmp\pal8v5.bmp + tests\Images\Input\Bmp\rgb16-565.bmp = tests\Images\Input\Bmp\rgb16-565.bmp + tests\Images\Input\Bmp\rgb16-565pal.bmp = tests\Images\Input\Bmp\rgb16-565pal.bmp + tests\Images\Input\Bmp\rgb16.bmp = tests\Images\Input\Bmp\rgb16.bmp + tests\Images\Input\Bmp\rgb16bfdef.bmp = tests\Images\Input\Bmp\rgb16bfdef.bmp + tests\Images\Input\Bmp\rgb24.bmp = tests\Images\Input\Bmp\rgb24.bmp + tests\Images\Input\Bmp\rgb32.bmp = tests\Images\Input\Bmp\rgb32.bmp + tests\Images\Input\Bmp\rgb32bf.bmp = tests\Images\Input\Bmp\rgb32bf.bmp + tests\Images\Input\Bmp\rgb32bfdef.bmp = tests\Images\Input\Bmp\rgb32bfdef.bmp + tests\Images\Input\Bmp\rgba32-1010102.bmp = tests\Images\Input\Bmp\rgba32-1010102.bmp + tests\Images\Input\Bmp\rgba32.bmp = tests\Images\Input\Bmp\rgba32.bmp + tests\Images\Input\Bmp\rgba32abf.bmp = tests\Images\Input\Bmp\rgba32abf.bmp + tests\Images\Input\Bmp\rgba32h56.bmp = tests\Images\Input\Bmp\rgba32h56.bmp + tests\Images\Input\Bmp\RunLengthEncoded-inverted.bmp = tests\Images\Input\Bmp\RunLengthEncoded-inverted.bmp + tests\Images\Input\Bmp\RunLengthEncoded.bmp = tests\Images\Input\Bmp\RunLengthEncoded.bmp + tests\Images\Input\Bmp\test16-inverted.bmp = tests\Images\Input\Bmp\test16-inverted.bmp + tests\Images\Input\Bmp\test16.bmp = tests\Images\Input\Bmp\test16.bmp + tests\Images\Input\Bmp\test8-inverted.bmp = tests\Images\Input\Bmp\test8-inverted.bmp + tests\Images\Input\Bmp\test8.bmp = tests\Images\Input\Bmp\test8.bmp + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Gif", "Gif", "{EE3FB0B3-1C31-41E9-93AB-BA800560A868}" + ProjectSection(SolutionItems) = preProject + tests\Images\Input\Gif\base_1x4.gif = tests\Images\Input\Gif\base_1x4.gif + tests\Images\Input\Gif\base_4x1.gif = tests\Images\Input\Gif\base_4x1.gif + tests\Images\Input\Gif\cheers.gif = tests\Images\Input\Gif\cheers.gif + tests\Images\Input\Gif\giphy.gif = tests\Images\Input\Gif\giphy.gif + tests\Images\Input\Gif\kumin.gif = tests\Images\Input\Gif\kumin.gif + tests\Images\Input\Gif\leo.gif = tests\Images\Input\Gif\leo.gif + tests\Images\Input\Gif\rings.gif = tests\Images\Input\Gif\rings.gif + tests\Images\Input\Gif\trans.gif = tests\Images\Input\Gif\trans.gif + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "issues", "issues", "{BF8DFDC1-CEE5-4A37-B216-D3085360C776}" + ProjectSection(SolutionItems) = preProject + tests\Images\Input\Gif\issues\issue403_baddescriptorwidth.gif = tests\Images\Input\Gif\issues\issue403_baddescriptorwidth.gif + tests\Images\Input\Gif\issues\issue405_badappextlength252-2.gif = tests\Images\Input\Gif\issues\issue405_badappextlength252-2.gif + tests\Images\Input\Gif\issues\issue405_badappextlength252.gif = tests\Images\Input\Gif\issues\issue405_badappextlength252.gif + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Jpg", "Jpg", "{DB21FED7-E8CB-4B00-9EB2-9144D32A590A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "baseline", "baseline", "{195BA3D3-3E9F-4BC5-AB40-5F9FEB638146}" + ProjectSection(SolutionItems) = preProject + tests\Images\Input\Jpg\baseline\AsianCarvingLowContrast.jpg = tests\Images\Input\Jpg\baseline\AsianCarvingLowContrast.jpg + tests\Images\Input\Jpg\baseline\badeof.jpg = tests\Images\Input\Jpg\baseline\badeof.jpg + tests\Images\Input\Jpg\baseline\badrst.jpg = tests\Images\Input\Jpg\baseline\badrst.jpg + tests\Images\Input\Jpg\baseline\Calliphora.jpg = tests\Images\Input\Jpg\baseline\Calliphora.jpg + tests\Images\Input\Jpg\baseline\cmyk.jpg = tests\Images\Input\Jpg\baseline\cmyk.jpg + tests\Images\Input\Jpg\baseline\exif.jpg = tests\Images\Input\Jpg\baseline\exif.jpg + tests\Images\Input\Jpg\baseline\Floorplan.jpg = tests\Images\Input\Jpg\baseline\Floorplan.jpg + tests\Images\Input\Jpg\baseline\gamma_dalai_lama_gray.jpg = tests\Images\Input\Jpg\baseline\gamma_dalai_lama_gray.jpg + tests\Images\Input\Jpg\baseline\Hiyamugi.jpg = tests\Images\Input\Jpg\baseline\Hiyamugi.jpg + tests\Images\Input\Jpg\baseline\jpeg400jfif.jpg = tests\Images\Input\Jpg\baseline\jpeg400jfif.jpg + tests\Images\Input\Jpg\baseline\jpeg420exif.jpg = tests\Images\Input\Jpg\baseline\jpeg420exif.jpg + tests\Images\Input\Jpg\baseline\jpeg420small.jpg = tests\Images\Input\Jpg\baseline\jpeg420small.jpg + tests\Images\Input\Jpg\baseline\jpeg444.jpg = tests\Images\Input\Jpg\baseline\jpeg444.jpg + tests\Images\Input\Jpg\baseline\Lake.jpg = tests\Images\Input\Jpg\baseline\Lake.jpg + tests\Images\Input\Jpg\baseline\MultiScanBaselineCMYK.jpg = tests\Images\Input\Jpg\baseline\MultiScanBaselineCMYK.jpg + tests\Images\Input\Jpg\baseline\ratio-1x1.jpg = tests\Images\Input\Jpg\baseline\ratio-1x1.jpg + tests\Images\Input\Jpg\baseline\Snake.jpg = tests\Images\Input\Jpg\baseline\Snake.jpg + tests\Images\Input\Jpg\baseline\testimgint.jpg = tests\Images\Input\Jpg\baseline\testimgint.jpg + tests\Images\Input\Jpg\baseline\testorig.jpg = tests\Images\Input\Jpg\baseline\testorig.jpg + tests\Images\Input\Jpg\baseline\testorig12.jpg = tests\Images\Input\Jpg\baseline\testorig12.jpg + tests\Images\Input\Jpg\baseline\turtle.jpg = tests\Images\Input\Jpg\baseline\turtle.jpg + tests\Images\Input\Jpg\baseline\ycck-subsample-1222.jpg = tests\Images\Input\Jpg\baseline\ycck-subsample-1222.jpg + tests\Images\Input\Jpg\baseline\ycck.jpg = tests\Images\Input\Jpg\baseline\ycck.jpg + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "JpegSnoopReports", "JpegSnoopReports", "{538F0EBD-4084-4EDB-93DD-6D35B733CA48}" + ProjectSection(SolutionItems) = preProject + tests\Images\Input\Jpg\baseline\JpegSnoopReports\badeof.jpg.txt = tests\Images\Input\Jpg\baseline\JpegSnoopReports\badeof.jpg.txt + tests\Images\Input\Jpg\baseline\JpegSnoopReports\badrst.jpg.txt = tests\Images\Input\Jpg\baseline\JpegSnoopReports\badrst.jpg.txt + tests\Images\Input\Jpg\baseline\JpegSnoopReports\Calliphora.jpg.txt = tests\Images\Input\Jpg\baseline\JpegSnoopReports\Calliphora.jpg.txt + tests\Images\Input\Jpg\baseline\JpegSnoopReports\cmyk.jpg.txt = tests\Images\Input\Jpg\baseline\JpegSnoopReports\cmyk.jpg.txt + tests\Images\Input\Jpg\baseline\JpegSnoopReports\exif.jpg.txt = tests\Images\Input\Jpg\baseline\JpegSnoopReports\exif.jpg.txt + tests\Images\Input\Jpg\baseline\JpegSnoopReports\Floorplan.jpg.txt = tests\Images\Input\Jpg\baseline\JpegSnoopReports\Floorplan.jpg.txt + tests\Images\Input\Jpg\baseline\JpegSnoopReports\gamma_dalai_lama_gray.jpg.txt = tests\Images\Input\Jpg\baseline\JpegSnoopReports\gamma_dalai_lama_gray.jpg.txt + tests\Images\Input\Jpg\baseline\JpegSnoopReports\Hiyamugi.jpg.txt = tests\Images\Input\Jpg\baseline\JpegSnoopReports\Hiyamugi.jpg.txt + tests\Images\Input\Jpg\baseline\JpegSnoopReports\jpeg400jfif.jpg.txt = tests\Images\Input\Jpg\baseline\JpegSnoopReports\jpeg400jfif.jpg.txt + tests\Images\Input\Jpg\baseline\JpegSnoopReports\jpeg420exif.jpg.txt = tests\Images\Input\Jpg\baseline\JpegSnoopReports\jpeg420exif.jpg.txt + tests\Images\Input\Jpg\baseline\JpegSnoopReports\jpeg420small.jpg.txt = tests\Images\Input\Jpg\baseline\JpegSnoopReports\jpeg420small.jpg.txt + tests\Images\Input\Jpg\baseline\JpegSnoopReports\jpeg444.jpg.txt = tests\Images\Input\Jpg\baseline\JpegSnoopReports\jpeg444.jpg.txt + tests\Images\Input\Jpg\baseline\JpegSnoopReports\Lake.jpg.txt = tests\Images\Input\Jpg\baseline\JpegSnoopReports\Lake.jpg.txt + tests\Images\Input\Jpg\baseline\JpegSnoopReports\MultiScanBaselineCMYK.jpg.txt = tests\Images\Input\Jpg\baseline\JpegSnoopReports\MultiScanBaselineCMYK.jpg.txt + tests\Images\Input\Jpg\baseline\JpegSnoopReports\ratio-1x1.jpg.txt = tests\Images\Input\Jpg\baseline\JpegSnoopReports\ratio-1x1.jpg.txt + tests\Images\Input\Jpg\baseline\JpegSnoopReports\Snake.jpg.txt = tests\Images\Input\Jpg\baseline\JpegSnoopReports\Snake.jpg.txt + tests\Images\Input\Jpg\baseline\JpegSnoopReports\testimgint.jpg.txt = tests\Images\Input\Jpg\baseline\JpegSnoopReports\testimgint.jpg.txt + tests\Images\Input\Jpg\baseline\JpegSnoopReports\testorig.jpg.txt = tests\Images\Input\Jpg\baseline\JpegSnoopReports\testorig.jpg.txt + tests\Images\Input\Jpg\baseline\JpegSnoopReports\turtle.jpg.txt = tests\Images\Input\Jpg\baseline\JpegSnoopReports\turtle.jpg.txt + tests\Images\Input\Jpg\baseline\JpegSnoopReports\ycck.jpg.txt = tests\Images\Input\Jpg\baseline\JpegSnoopReports\ycck.jpg.txt + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "issues", "issues", "{5C9B689F-B96D-47BE-A208-C23B1B2A8570}" + ProjectSection(SolutionItems) = preProject + tests\Images\Input\Jpg\issues\Issue159-MissingFF00-Progressive-Bedroom.jpg = tests\Images\Input\Jpg\issues\Issue159-MissingFF00-Progressive-Bedroom.jpg + tests\Images\Input\Jpg\issues\Issue159-MissingFF00-Progressive-Girl.jpg = tests\Images\Input\Jpg\issues\Issue159-MissingFF00-Progressive-Girl.jpg + tests\Images\Input\Jpg\issues\Issue178-BadCoeffsProgressive-Lemon.jpg = tests\Images\Input\Jpg\issues\Issue178-BadCoeffsProgressive-Lemon.jpg + tests\Images\Input\Jpg\issues\Issue214-CriticalEOF.jpg = tests\Images\Input\Jpg\issues\Issue214-CriticalEOF.jpg + tests\Images\Input\Jpg\issues\Issue385-BadZigZag-Progressive.jpg = tests\Images\Input\Jpg\issues\Issue385-BadZigZag-Progressive.jpg + tests\Images\Input\Jpg\issues\Issue394-MultiHuffmanBaseline-Speakers.jpg = tests\Images\Input\Jpg\issues\Issue394-MultiHuffmanBaseline-Speakers.jpg + tests\Images\Input\Jpg\issues\Issue517-No-EOI-Progressive.jpg = tests\Images\Input\Jpg\issues\Issue517-No-EOI-Progressive.jpg + tests\Images\Input\Jpg\issues\Issue518-Bad-RST-Progressive.jpg = tests\Images\Input\Jpg\issues\Issue518-Bad-RST-Progressive.jpg + tests\Images\Input\Jpg\issues\Issue520-InvalidCast.jpg = tests\Images\Input\Jpg\issues\Issue520-InvalidCast.jpg + tests\Images\Input\Jpg\issues\Issue624-DhtHasWrongLength-Progressive-N.jpg = tests\Images\Input\Jpg\issues\Issue624-DhtHasWrongLength-Progressive-N.jpg + tests\Images\Input\Jpg\issues\Issue694-Decode-Exif-OutOfRange.jpg = tests\Images\Input\Jpg\issues\Issue694-Decode-Exif-OutOfRange.jpg + tests\Images\Input\Jpg\issues\Issue695-Invalid-EOI.jpg = tests\Images\Input\Jpg\issues\Issue695-Invalid-EOI.jpg + tests\Images\Input\Jpg\issues\Issue696-Resize-Exif-OutOfRange.jpg = tests\Images\Input\Jpg\issues\Issue696-Resize-Exif-OutOfRange.jpg + tests\Images\Input\Jpg\issues\Issue721-InvalidAPP0.jpg = tests\Images\Input\Jpg\issues\Issue721-InvalidAPP0.jpg + tests\Images\Input\Jpg\issues\Issue723-Ordered-Interleaved-Progressive-A.jpg = tests\Images\Input\Jpg\issues\Issue723-Ordered-Interleaved-Progressive-A.jpg + tests\Images\Input\Jpg\issues\Issue723-Ordered-Interleaved-Progressive-B.jpg = tests\Images\Input\Jpg\issues\Issue723-Ordered-Interleaved-Progressive-B.jpg + tests\Images\Input\Jpg\issues\Issue723-Ordered-Interleaved-Progressive-C.jpg = tests\Images\Input\Jpg\issues\Issue723-Ordered-Interleaved-Progressive-C.jpg + tests\Images\Input\Jpg\issues\issue750-exif-load.jpg = tests\Images\Input\Jpg\issues\issue750-exif-load.jpg + tests\Images\Input\Jpg\issues\issue750-exif-tranform.jpg = tests\Images\Input\Jpg\issues\issue750-exif-tranform.jpg + tests\Images\Input\Jpg\issues\Issue845-Incorrect-Quality99.jpg = tests\Images\Input\Jpg\issues\Issue845-Incorrect-Quality99.jpg + tests\Images\Input\Jpg\issues\issue855-incorrect-colorspace.jpg = tests\Images\Input\Jpg\issues\issue855-incorrect-colorspace.jpg + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "fuzz", "fuzz", "{516A3532-6AC2-417B-AD79-9BD5D0D378A0}" + ProjectSection(SolutionItems) = preProject + tests\Images\Input\Jpg\issues\fuzz\Issue797-NullReferenceException.jpg = tests\Images\Input\Jpg\issues\fuzz\Issue797-NullReferenceException.jpg + tests\Images\Input\Jpg\issues\fuzz\Issue798-AccessViolationException.jpg = tests\Images\Input\Jpg\issues\fuzz\Issue798-AccessViolationException.jpg + tests\Images\Input\Jpg\issues\fuzz\Issue821-DivideByZeroException.jpg = tests\Images\Input\Jpg\issues\fuzz\Issue821-DivideByZeroException.jpg + tests\Images\Input\Jpg\issues\fuzz\Issue822-DivideByZeroException.jpg = tests\Images\Input\Jpg\issues\fuzz\Issue822-DivideByZeroException.jpg + tests\Images\Input\Jpg\issues\fuzz\Issue823-NullReferenceException.jpg = tests\Images\Input\Jpg\issues\fuzz\Issue823-NullReferenceException.jpg + tests\Images\Input\Jpg\issues\fuzz\Issue824-IndexOutOfRangeException-A.jpg = tests\Images\Input\Jpg\issues\fuzz\Issue824-IndexOutOfRangeException-A.jpg + tests\Images\Input\Jpg\issues\fuzz\Issue824-IndexOutOfRangeException-B.jpg = tests\Images\Input\Jpg\issues\fuzz\Issue824-IndexOutOfRangeException-B.jpg + tests\Images\Input\Jpg\issues\fuzz\Issue824-IndexOutOfRangeException-C.jpg = tests\Images\Input\Jpg\issues\fuzz\Issue824-IndexOutOfRangeException-C.jpg + tests\Images\Input\Jpg\issues\fuzz\Issue824-IndexOutOfRangeException-D.jpg = tests\Images\Input\Jpg\issues\fuzz\Issue824-IndexOutOfRangeException-D.jpg + tests\Images\Input\Jpg\issues\fuzz\Issue824-IndexOutOfRangeException-E.jpg = tests\Images\Input\Jpg\issues\fuzz\Issue824-IndexOutOfRangeException-E.jpg + tests\Images\Input\Jpg\issues\fuzz\Issue824-IndexOutOfRangeException-F.jpg = tests\Images\Input\Jpg\issues\fuzz\Issue824-IndexOutOfRangeException-F.jpg + tests\Images\Input\Jpg\issues\fuzz\Issue824-IndexOutOfRangeException-G.jpg = tests\Images\Input\Jpg\issues\fuzz\Issue824-IndexOutOfRangeException-G.jpg + tests\Images\Input\Jpg\issues\fuzz\Issue824-IndexOutOfRangeException-H.jpg = tests\Images\Input\Jpg\issues\fuzz\Issue824-IndexOutOfRangeException-H.jpg + tests\Images\Input\Jpg\issues\fuzz\Issue825-ArgumentOutOfRangeException-A.jpg = tests\Images\Input\Jpg\issues\fuzz\Issue825-ArgumentOutOfRangeException-A.jpg + tests\Images\Input\Jpg\issues\fuzz\Issue825-ArgumentOutOfRangeException-B.jpg = tests\Images\Input\Jpg\issues\fuzz\Issue825-ArgumentOutOfRangeException-B.jpg + tests\Images\Input\Jpg\issues\fuzz\Issue825-ArgumentOutOfRangeException-C.jpg = tests\Images\Input\Jpg\issues\fuzz\Issue825-ArgumentOutOfRangeException-C.jpg + tests\Images\Input\Jpg\issues\fuzz\Issue825-ArgumentOutOfRangeException-D.jpg = tests\Images\Input\Jpg\issues\fuzz\Issue825-ArgumentOutOfRangeException-D.jpg + tests\Images\Input\Jpg\issues\fuzz\Issue826-ArgumentException-A.jpg = tests\Images\Input\Jpg\issues\fuzz\Issue826-ArgumentException-A.jpg + tests\Images\Input\Jpg\issues\fuzz\Issue826-ArgumentException-B.jpg = tests\Images\Input\Jpg\issues\fuzz\Issue826-ArgumentException-B.jpg + tests\Images\Input\Jpg\issues\fuzz\Issue826-ArgumentException-C.jpg = tests\Images\Input\Jpg\issues\fuzz\Issue826-ArgumentException-C.jpg + tests\Images\Input\Jpg\issues\fuzz\Issue827-AccessViolationException.jpg = tests\Images\Input\Jpg\issues\fuzz\Issue827-AccessViolationException.jpg + tests\Images\Input\Jpg\issues\fuzz\Issue839-ExecutionEngineException.jpg = tests\Images\Input\Jpg\issues\fuzz\Issue839-ExecutionEngineException.jpg + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "JpegSnoopReports", "JpegSnoopReports", "{714CDEA1-9AE6-4F76-B8B1-A7DB8C1DB82F}" + ProjectSection(SolutionItems) = preProject + tests\Images\Input\Jpg\issues\JpegSnoopReports\Issue159-MissingFF00-Progressive-Bedroom.jpg.txt = tests\Images\Input\Jpg\issues\JpegSnoopReports\Issue159-MissingFF00-Progressive-Bedroom.jpg.txt + tests\Images\Input\Jpg\issues\JpegSnoopReports\Issue159-MissingFF00-Progressive-Girl.jpg.txt = tests\Images\Input\Jpg\issues\JpegSnoopReports\Issue159-MissingFF00-Progressive-Girl.jpg.txt + tests\Images\Input\Jpg\issues\JpegSnoopReports\Issue178-BadCoeffsProgressive-Lemon.jpg.txt = tests\Images\Input\Jpg\issues\JpegSnoopReports\Issue178-BadCoeffsProgressive-Lemon.jpg.txt + tests\Images\Input\Jpg\issues\JpegSnoopReports\Issue214-CriticalEOF .jpg.txt = tests\Images\Input\Jpg\issues\JpegSnoopReports\Issue214-CriticalEOF .jpg.txt + tests\Images\Input\Jpg\issues\JpegSnoopReports\Issue385-BadZigZag-Progressive.jpg.txt = tests\Images\Input\Jpg\issues\JpegSnoopReports\Issue385-BadZigZag-Progressive.jpg.txt + tests\Images\Input\Jpg\issues\JpegSnoopReports\Issue394-MultiHuffmanBaseline-Speakers.jpg.txt = tests\Images\Input\Jpg\issues\JpegSnoopReports\Issue394-MultiHuffmanBaseline-Speakers.jpg.txt + tests\Images\Input\Jpg\issues\JpegSnoopReports\Issue517-No-EOI-Progressive.jpg.txt = tests\Images\Input\Jpg\issues\JpegSnoopReports\Issue517-No-EOI-Progressive.jpg.txt + tests\Images\Input\Jpg\issues\JpegSnoopReports\Issue518-Bad-RST-Progressive.jpg.txt = tests\Images\Input\Jpg\issues\JpegSnoopReports\Issue518-Bad-RST-Progressive.jpg.txt + tests\Images\Input\Jpg\issues\JpegSnoopReports\Issue520-InvalidCast.jpg.txt = tests\Images\Input\Jpg\issues\JpegSnoopReports\Issue520-InvalidCast.jpg.txt + tests\Images\Input\Jpg\issues\JpegSnoopReports\Issue624-DhtHasWrongLength-Progressive-N.jpg.txt = tests\Images\Input\Jpg\issues\JpegSnoopReports\Issue624-DhtHasWrongLength-Progressive-N.jpg.txt + tests\Images\Input\Jpg\issues\JpegSnoopReports\Issue694-Decode-Exif-OutOfRange.jpg.txt = tests\Images\Input\Jpg\issues\JpegSnoopReports\Issue694-Decode-Exif-OutOfRange.jpg.txt + tests\Images\Input\Jpg\issues\JpegSnoopReports\Issue695-Invalid-EOI.jpg.txt = tests\Images\Input\Jpg\issues\JpegSnoopReports\Issue695-Invalid-EOI.jpg.txt + tests\Images\Input\Jpg\issues\JpegSnoopReports\Issue696-Resize-Exif-OutOfRange.jpg.txt = tests\Images\Input\Jpg\issues\JpegSnoopReports\Issue696-Resize-Exif-OutOfRange.jpg.txt + tests\Images\Input\Jpg\issues\JpegSnoopReports\Issue721-InvalidAPP0.jpg.txt = tests\Images\Input\Jpg\issues\JpegSnoopReports\Issue721-InvalidAPP0.jpg.txt + tests\Images\Input\Jpg\issues\JpegSnoopReports\Issue723-Ordered-Interleaved-Progressive-A.jpg.txt = tests\Images\Input\Jpg\issues\JpegSnoopReports\Issue723-Ordered-Interleaved-Progressive-A.jpg.txt + tests\Images\Input\Jpg\issues\JpegSnoopReports\Issue723-Ordered-Interleaved-Progressive-B.jpg.txt = tests\Images\Input\Jpg\issues\JpegSnoopReports\Issue723-Ordered-Interleaved-Progressive-B.jpg.txt + tests\Images\Input\Jpg\issues\JpegSnoopReports\Issue723-Ordered-Interleaved-Progressive-C.jpg.txt = tests\Images\Input\Jpg\issues\JpegSnoopReports\Issue723-Ordered-Interleaved-Progressive-C.jpg.txt + tests\Images\Input\Jpg\issues\JpegSnoopReports\issue750-exif-load.jpg.txt = tests\Images\Input\Jpg\issues\JpegSnoopReports\issue750-exif-load.jpg.txt + tests\Images\Input\Jpg\issues\JpegSnoopReports\issue750-exif-tranform.jpg.txt = tests\Images\Input\Jpg\issues\JpegSnoopReports\issue750-exif-tranform.jpg.txt + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "progressive", "progressive", "{6458AFCB-A159-47D5-8F2B-50C95C0915E0}" + ProjectSection(SolutionItems) = preProject + tests\Images\Input\Jpg\progressive\BadEofProgressive.jpg = tests\Images\Input\Jpg\progressive\BadEofProgressive.jpg + tests\Images\Input\Jpg\progressive\ExifUndefType.jpg = tests\Images\Input\Jpg\progressive\ExifUndefType.jpg + tests\Images\Input\Jpg\progressive\fb.jpg = tests\Images\Input\Jpg\progressive\fb.jpg + tests\Images\Input\Jpg\progressive\Festzug.jpg = tests\Images\Input\Jpg\progressive\Festzug.jpg + tests\Images\Input\Jpg\progressive\progress.jpg = tests\Images\Input\Jpg\progressive\progress.jpg + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "JpegSnoopReports", "JpegSnoopReports", "{39F5197B-CF6C-41A5-9739-7F97E78BB104}" + ProjectSection(SolutionItems) = preProject + tests\Images\Input\Jpg\progressive\JpegSnoopReports\BadEofProgressive.jpg.txt = tests\Images\Input\Jpg\progressive\JpegSnoopReports\BadEofProgressive.jpg.txt + tests\Images\Input\Jpg\progressive\JpegSnoopReports\ExifUndefType.jpg.txt = tests\Images\Input\Jpg\progressive\JpegSnoopReports\ExifUndefType.jpg.txt + tests\Images\Input\Jpg\progressive\JpegSnoopReports\fb.jpg.txt = tests\Images\Input\Jpg\progressive\JpegSnoopReports\fb.jpg.txt + tests\Images\Input\Jpg\progressive\JpegSnoopReports\Festzug.jpg.txt = tests\Images\Input\Jpg\progressive\JpegSnoopReports\Festzug.jpg.txt + tests\Images\Input\Jpg\progressive\JpegSnoopReports\progress.jpg.txt = tests\Images\Input\Jpg\progressive\JpegSnoopReports\progress.jpg.txt + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Png", "Png", "{E1C42A6F-913B-4A7B-B1A8-2BB62843B254}" + ProjectSection(SolutionItems) = preProject + tests\Images\Input\Png\banner7-adam.png = tests\Images\Input\Png\banner7-adam.png + tests\Images\Input\Png\banner8-index.png = tests\Images\Input\Png\banner8-index.png + tests\Images\Input\Png\big-corrupted-chunk.png = tests\Images\Input\Png\big-corrupted-chunk.png + tests\Images\Input\Png\Bike.png = tests\Images\Input\Png\Bike.png + tests\Images\Input\Png\BikeGrayscale.png = tests\Images\Input\Png\BikeGrayscale.png + tests\Images\Input\Png\blur.png = tests\Images\Input\Png\blur.png + tests\Images\Input\Png\bpp1.png = tests\Images\Input\Png\bpp1.png + tests\Images\Input\Png\CalliphoraPartial.png = tests\Images\Input\Png\CalliphoraPartial.png + tests\Images\Input\Png\CalliphoraPartialGrayscale.png = tests\Images\Input\Png\CalliphoraPartialGrayscale.png + tests\Images\Input\Png\chunklength1.png = tests\Images\Input\Png\chunklength1.png + tests\Images\Input\Png\chunklength2.png = tests\Images\Input\Png\chunklength2.png + tests\Images\Input\Png\cross.png = tests\Images\Input\Png\cross.png + tests\Images\Input\Png\ducky.png = tests\Images\Input\Png\ducky.png + tests\Images\Input\Png\filter0.png = tests\Images\Input\Png\filter0.png + tests\Images\Input\Png\filter1.png = tests\Images\Input\Png\filter1.png + tests\Images\Input\Png\filter2.png = tests\Images\Input\Png\filter2.png + tests\Images\Input\Png\filter3.png = tests\Images\Input\Png\filter3.png + tests\Images\Input\Png\filter4.png = tests\Images\Input\Png\filter4.png + tests\Images\Input\Png\filterVar.png = tests\Images\Input\Png\filterVar.png + tests\Images\Input\Png\gray-1-trns.png = tests\Images\Input\Png\gray-1-trns.png + tests\Images\Input\Png\gray-16-tRNS-interlaced.png = tests\Images\Input\Png\gray-16-tRNS-interlaced.png + tests\Images\Input\Png\gray-16.png = tests\Images\Input\Png\gray-16.png + tests\Images\Input\Png\gray-2-tRNS.png = tests\Images\Input\Png\gray-2-tRNS.png + tests\Images\Input\Png\gray-4-tRNS.png = tests\Images\Input\Png\gray-4-tRNS.png + tests\Images\Input\Png\gray-8-tRNS.png = tests\Images\Input\Png\gray-8-tRNS.png + tests\Images\Input\Png\gray-alpha-16.png = tests\Images\Input\Png\gray-alpha-16.png + tests\Images\Input\Png\gray-alpha-8.png = tests\Images\Input\Png\gray-alpha-8.png + tests\Images\Input\Png\gray_4bpp.png = tests\Images\Input\Png\gray_4bpp.png + tests\Images\Input\Png\icon.png = tests\Images\Input\Png\icon.png + tests\Images\Input\Png\iftbbn0g01.png = tests\Images\Input\Png\iftbbn0g01.png + tests\Images\Input\Png\iftbbn0g02.png = tests\Images\Input\Png\iftbbn0g02.png + tests\Images\Input\Png\iftbbn0g04.png = tests\Images\Input\Png\iftbbn0g04.png + tests\Images\Input\Png\indexed.png = tests\Images\Input\Png\indexed.png + tests\Images\Input\Png\interlaced.png = tests\Images\Input\Png\interlaced.png + tests\Images\Input\Png\kaboom.png = tests\Images\Input\Png\kaboom.png + tests\Images\Input\Png\low-variance.png = tests\Images\Input\Png\low-variance.png + tests\Images\Input\Png\palette-8bpp.png = tests\Images\Input\Png\palette-8bpp.png + tests\Images\Input\Png\pd-dest.png = tests\Images\Input\Png\pd-dest.png + tests\Images\Input\Png\pd-source.png = tests\Images\Input\Png\pd-source.png + tests\Images\Input\Png\pd.png = tests\Images\Input\Png\pd.png + tests\Images\Input\Png\pl.png = tests\Images\Input\Png\pl.png + tests\Images\Input\Png\pp.png = tests\Images\Input\Png\pp.png + tests\Images\Input\Png\rainbow.png = tests\Images\Input\Png\rainbow.png + tests\Images\Input\Png\ratio-1x4.png = tests\Images\Input\Png\ratio-1x4.png + tests\Images\Input\Png\ratio-4x1.png = tests\Images\Input\Png\ratio-4x1.png + tests\Images\Input\Png\rgb-16-alpha.png = tests\Images\Input\Png\rgb-16-alpha.png + tests\Images\Input\Png\rgb-16-tRNS.png = tests\Images\Input\Png\rgb-16-tRNS.png + tests\Images\Input\Png\rgb-48bpp-interlaced.png = tests\Images\Input\Png\rgb-48bpp-interlaced.png + tests\Images\Input\Png\rgb-48bpp.png = tests\Images\Input\Png\rgb-48bpp.png + tests\Images\Input\Png\rgb-8-tRNS.png = tests\Images\Input\Png\rgb-8-tRNS.png + tests\Images\Input\Png\rollsroyce.png = tests\Images\Input\Png\rollsroyce.png + tests\Images\Input\Png\SnakeGame.png = tests\Images\Input\Png\SnakeGame.png + tests\Images\Input\Png\splash-interlaced.png = tests\Images\Input\Png\splash-interlaced.png + tests\Images\Input\Png\splash.png = tests\Images\Input\Png\splash.png + tests\Images\Input\Png\versioning-1_1.png = tests\Images\Input\Png\versioning-1_1.png + tests\Images\Input\Png\versioning-1_2.png = tests\Images\Input\Png\versioning-1_2.png + tests\Images\Input\Png\vim16x16_1.png = tests\Images\Input\Png\vim16x16_1.png + tests\Images\Input\Png\vim16x16_2.png = tests\Images\Input\Png\vim16x16_2.png + tests\Images\Input\Png\zlib-overflow.png = tests\Images\Input\Png\zlib-overflow.png + EndProjectSection +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Tests", "tests\ImageSharp.Tests\ImageSharp.Tests.csproj", "{EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Benchmarks", "tests\ImageSharp.Benchmarks\ImageSharp.Benchmarks.csproj", "{2BF743D8-2A06-412D-96D7-F448F00C5EA5}" @@ -123,9 +432,25 @@ Global HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {9E574A07-F879-4811-9C41-5CBDC6BAFDB7} = {815C0625-CD3D-440F-9F80-2D83856AB7AE} + {FBE8C1AD-5AEC-4514-9B64-091D8E145865} = {1799C43E-5C54-4A8F-8D64-B1475241DB0D} + {2B02E303-7CC6-4E15-97EE-DBE86B287553} = {E919DF0B-2607-4462-8FC0-5C98FE50F8C9} {2AA31A1F-142C-43F4-8687-09ABCA4B3A26} = {815C0625-CD3D-440F-9F80-2D83856AB7AE} {2E33181E-6E28-4662-A801-E2E7DC206029} = {815C0625-CD3D-440F-9F80-2D83856AB7AE} + {D4C5EC58-F8E6-4636-B9EE-C99D2578E5C6} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} + {FA55F5DE-11A6-487D-ABA4-BC93A02717DD} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} + {9DA226A1-8656-49A8-A58A-A8B5C081AD66} = {FA55F5DE-11A6-487D-ABA4-BC93A02717DD} + {1A82C5F6-90E0-4E97-BE16-A825C046B493} = {9DA226A1-8656-49A8-A58A-A8B5C081AD66} + {EE3FB0B3-1C31-41E9-93AB-BA800560A868} = {9DA226A1-8656-49A8-A58A-A8B5C081AD66} + {BF8DFDC1-CEE5-4A37-B216-D3085360C776} = {EE3FB0B3-1C31-41E9-93AB-BA800560A868} + {DB21FED7-E8CB-4B00-9EB2-9144D32A590A} = {9DA226A1-8656-49A8-A58A-A8B5C081AD66} + {195BA3D3-3E9F-4BC5-AB40-5F9FEB638146} = {DB21FED7-E8CB-4B00-9EB2-9144D32A590A} + {538F0EBD-4084-4EDB-93DD-6D35B733CA48} = {195BA3D3-3E9F-4BC5-AB40-5F9FEB638146} + {5C9B689F-B96D-47BE-A208-C23B1B2A8570} = {DB21FED7-E8CB-4B00-9EB2-9144D32A590A} + {516A3532-6AC2-417B-AD79-9BD5D0D378A0} = {5C9B689F-B96D-47BE-A208-C23B1B2A8570} + {714CDEA1-9AE6-4F76-B8B1-A7DB8C1DB82F} = {5C9B689F-B96D-47BE-A208-C23B1B2A8570} + {6458AFCB-A159-47D5-8F2B-50C95C0915E0} = {DB21FED7-E8CB-4B00-9EB2-9144D32A590A} + {39F5197B-CF6C-41A5-9739-7F97E78BB104} = {6458AFCB-A159-47D5-8F2B-50C95C0915E0} + {E1C42A6F-913B-4A7B-B1A8-2BB62843B254} = {9DA226A1-8656-49A8-A58A-A8B5C081AD66} {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} {2BF743D8-2A06-412D-96D7-F448F00C5EA5} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} {561B880A-D9EE-44EF-90F5-817C54A9D9AB} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} diff --git a/NuGet.config b/NuGet.config deleted file mode 100644 index 322105d4d4..0000000000 --- a/NuGet.config +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/src/Directory.Build.props b/src/Directory.Build.props new file mode 100644 index 0000000000..cd3d5e8cb3 --- /dev/null +++ b/src/Directory.Build.props @@ -0,0 +1,41 @@ + + + + + + + $(MSBuildAllProjects);$(MSBuildThisFileDirectory)..\Directory.Build.props + src + + + + + + $(MSBuildThisFileDirectory)..\standards\SixLabors.ruleset + true + + + + + + + + + + + + + + + + + + + diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets new file mode 100644 index 0000000000..c0e01ae586 --- /dev/null +++ b/src/Directory.Build.targets @@ -0,0 +1,55 @@ + + + + + + + $(MSBuildAllProjects);$(MSBuildThisFileDirectory)..\Directory.Build.targets + + + + + + $(IntermediateOutputPath)$(MSBuildProjectName).InternalsVisibleTo$(DefaultLanguageSourceExtension) + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj index f9181b45da..a092e3604b 100644 --- a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj +++ b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj @@ -1,55 +1,23 @@ - + + + + SixLabors.ImageSharp.Drawing SixLabors.ImageSharp.Drawing - SixLabors and contributors - Six Labors - Copyright (c) Six Labors and contributors. - SixLabors.ImageSharp An extension to ImageSharp that allows the drawing of images, paths, and text. - en - - $(packageversion) - 0.0.1 - - netcoreapp2.1;netstandard1.3;netstandard2.0 - - 7.3 - true - true - SixLabors.ImageSharp.Drawing SixLabors.ImageSharp.Drawing Image Draw Shape Path Font - https://raw.githubusercontent.com/SixLabors/Branding/master/icons/imagesharp/sixlabors.imagesharp.128.png - https://github.com/SixLabors/ImageSharp - http://www.apache.org/licenses/LICENSE-2.0 - git - https://github.com/SixLabors/ImageSharp - full - portable - True + SixLabors.ImageSharp + netcoreapp2.1;netstandard1.3;netstandard2.0 - + + - - - - - - - - - ..\..\standards\SixLabors.ruleset - SixLabors.ImageSharp - - - - true - - \ No newline at end of file + diff --git a/src/ImageSharp.Drawing/Properties/AssemblyInfo.cs b/src/ImageSharp.Drawing/Properties/AssemblyInfo.cs deleted file mode 100644 index 2891598b9b..0000000000 --- a/src/ImageSharp.Drawing/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,4 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -// Common values read from `AssemblyInfo.Common.cs` diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index 832a6ce3f9..a3d614a067 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -1,33 +1,14 @@ - + + + + SixLabors.ImageSharp SixLabors.ImageSharp - Six Labors and contributors - Six Labors - Copyright (c) Six Labors and contributors. - SixLabors.ImageSharp A cross-platform library for the processing of image files; written in C# - en - - $(packageversion) - 0.0.1 - - netcoreapp2.1;netstandard1.3;netstandard2.0;net472 - - true - true - SixLabors.ImageSharp SixLabors.ImageSharp Image Resize Crop Gif Jpg Jpeg Bitmap Png Core - https://raw.githubusercontent.com/SixLabors/Branding/master/icons/imagesharp/sixlabors.imagesharp.128.png - https://github.com/SixLabors/ImageSharp - http://www.apache.org/licenses/LICENSE-2.0 - git - https://github.com/SixLabors/ImageSharp - full - portable - True - IOperation - 7.3 + SixLabors.ImageSharp + netcoreapp2.1;netstandard1.3;netstandard2.0;net472 @@ -35,93 +16,7 @@ - - - - - - - - - - - - - - - - - - - ..\..\standards\SixLabors.ruleset - SixLabors.ImageSharp - - - - true - - - - - TextTemplatingFileGenerator - Block8x8F.Generated.cs - - - TextTemplatingFileGenerator - GenericBlock8x8.Generated.cs - - - TextTemplatingFileGenerator - Block8x8F.Generated.cs - - - TextTemplatingFileGenerator - PixelOperations{TPixel}.Generated.cs - - - TextTemplatingFileGenerator - Argb32.PixelOperations.Generated.cs - - - TextTemplatingFileGenerator - Bgr24.PixelOperations.Generated.cs - - - TextTemplatingFileGenerator - Bgra32.PixelOperations.Generated.cs - - - TextTemplatingFileGenerator - Gray8.PixelOperations.Generated.cs - - - TextTemplatingFileGenerator - Gray16.PixelOperations.Generated.cs - - - TextTemplatingFileGenerator - Rgb24.PixelOperations.Generated.cs - - - TextTemplatingFileGenerator - Rgba32.PixelOperations.Generated.cs - - - TextTemplatingFileGenerator - Rgb48.PixelOperations.Generated.cs - - - TextTemplatingFileGenerator - Rgba64.PixelOperations.Generated.cs - - - PorterDuffFunctions.Generated.cs - TextTemplatingFileGenerator - - - DefaultPixelBlenders.Generated.cs - TextTemplatingFileGenerator - + @@ -203,6 +98,87 @@ - + + + + + + TextTemplatingFileGenerator + Block8x8F.Generated.cs + + + TextTemplatingFileGenerator + GenericBlock8x8.Generated.cs + + + TextTemplatingFileGenerator + Block8x8F.Generated.cs + + + TextTemplatingFileGenerator + PixelOperations{TPixel}.Generated.cs + + + TextTemplatingFileGenerator + Argb32.PixelOperations.Generated.cs + + + TextTemplatingFileGenerator + Bgr24.PixelOperations.Generated.cs + + + TextTemplatingFileGenerator + Bgra32.PixelOperations.Generated.cs + + + TextTemplatingFileGenerator + Gray8.PixelOperations.Generated.cs + + + TextTemplatingFileGenerator + Gray16.PixelOperations.Generated.cs + + + TextTemplatingFileGenerator + Rgb24.PixelOperations.Generated.cs + + + TextTemplatingFileGenerator + Rgba32.PixelOperations.Generated.cs + + + TextTemplatingFileGenerator + Rgb48.PixelOperations.Generated.cs + + + TextTemplatingFileGenerator + Rgba64.PixelOperations.Generated.cs + + + PorterDuffFunctions.Generated.cs + TextTemplatingFileGenerator + + + DefaultPixelBlenders.Generated.cs + TextTemplatingFileGenerator + + + + + + + + + + + + + + - \ No newline at end of file + + + + + + diff --git a/src/ImageSharp/Properties/AssemblyInfo.cs b/src/ImageSharp/Properties/AssemblyInfo.cs index f4459887f0..2862b45851 100644 --- a/src/ImageSharp/Properties/AssemblyInfo.cs +++ b/src/ImageSharp/Properties/AssemblyInfo.cs @@ -3,12 +3,9 @@ using System.Runtime.CompilerServices; -// Ensure the other projects can see the internal helpers -[assembly: InternalsVisibleTo("SixLabors.ImageSharp.Drawing")] - // Redundant suppressing of SA1413 for Rider. [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage( "StyleCop.CSharp.MaintainabilityRules", "SA1413:UseTrailingCommasInMultiLineInitializers", - Justification = "Follows SixLabors.ruleset")] \ No newline at end of file + Justification = "Follows SixLabors.ruleset")] diff --git a/src/Shared/AssemblyInfo.Common.cs b/src/Shared/AssemblyInfo.Common.cs deleted file mode 100644 index 82e1dac6c4..0000000000 --- a/src/Shared/AssemblyInfo.Common.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Reflection; -using System.Resources; -using System.Runtime.CompilerServices; - -// Ensure the internals can be built and tested. -[assembly: InternalsVisibleTo("SixLabors.ImageSharp.Drawing")] -[assembly: InternalsVisibleTo("ImageSharp.Benchmarks")] -[assembly: InternalsVisibleTo("SixLabors.ImageSharp.Tests")] -[assembly: InternalsVisibleTo("SixLabors.ImageSharp.Sandbox46")] - -[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] -[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKeyToken=null")] diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props new file mode 100644 index 0000000000..97bd9b6e7c --- /dev/null +++ b/tests/Directory.Build.props @@ -0,0 +1,30 @@ + + + + + + + $(MSBuildAllProjects);$(MSBuildThisFileDirectory)..\Directory.Build.props + tests + + + + CS0618;$(NoWarn) + + + + + + + + + + + diff --git a/tests/Directory.Build.targets b/tests/Directory.Build.targets new file mode 100644 index 0000000000..f8a4936e23 --- /dev/null +++ b/tests/Directory.Build.targets @@ -0,0 +1,19 @@ + + + + + + + $(MSBuildAllProjects);$(MSBuildThisFileDirectory)..\Directory.Build.targets + + + + + diff --git a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj index 2a5408da17..1e951f5d0e 100644 --- a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj +++ b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj @@ -1,29 +1,37 @@ - + + + - - netcoreapp2.1 + ImageSharp.Benchmarks Exe - True SixLabors.ImageSharp.Benchmarks - ImageSharp.Benchmarks - 7.3 + netcoreapp2.1;net472 - + + win7-x64 false + - - - - - + + + + + + + + + + + - + - \ No newline at end of file + + diff --git a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj index 6569dc002e..fc94668e11 100644 --- a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj +++ b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj @@ -1,30 +1,28 @@ - + + + - Exe - net472 - win7-x64 - True - false SixLabors.ImageSharp.Sandbox46 A cross-platform library for processing of image files written in C# - Copyright © James Jackson-South and contributors. - James Jackson-South and contributors - James Jackson-South + Exe + false SixLabors.ImageSharp.Sandbox46 - 7.3 + win7-x64 + net472 + - - + + - - - + + + - - - + + - \ No newline at end of file + + diff --git a/tests/ImageSharp.Sandbox46/Properties/AssemblyInfo.cs b/tests/ImageSharp.Sandbox46/Properties/AssemblyInfo.cs deleted file mode 100644 index a10fc12fe7..0000000000 --- a/tests/ImageSharp.Sandbox46/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("96188137-5fa6-4924-ab6e-4eff79c6e0bb")] diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index 0b727f30ce..406719ab4f 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -1,48 +1,24 @@ - + + + - netcoreapp2.1;net462;net472 - True - latest - full - portable - True - SixLabors.ImageSharp.Tests SixLabors.ImageSharp.Tests AnyCPU;x64;x86 + SixLabors.ImageSharp.Tests + netcoreapp2.1;net462;net472 - - false - - - - false - - - - false - - + - + + + + - + - - - - - - - - - - - - - - - + + PreserveNewest @@ -54,4 +30,11 @@ PreserveNewest + + + + + + + From 4a41c5b4efe85f56e95d8b7b9294036f6860d1bf Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 4 Jun 2019 13:26:39 +1000 Subject: [PATCH 212/223] Fix build/test (#923) --- Directory.Build.props | 3 ++- Directory.Build.targets | 2 +- standards | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 9c92c14b8a..6e3cc9b3bf 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -30,8 +30,9 @@ true + - true + false diff --git a/Directory.Build.targets b/Directory.Build.targets index 3504398794..d1183e5d46 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -25,7 +25,7 @@ - + diff --git a/standards b/standards index dd83f64963..8b085c0ec4 160000 --- a/standards +++ b/standards @@ -1 +1 @@ -Subproject commit dd83f649638c6333984a757c01be6ec294e6b63c +Subproject commit 8b085c0ec4fb64797b9965741f7138f8f66a6b44 From 4f76e3b49c60c14857879ed9e4eecacb98edfbbf Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 4 Jun 2019 15:41:45 +1000 Subject: [PATCH 213/223] Prevent zigzag overflow. Fix #922 --- .../Formats/Jpeg/Components/ZigZag.cs | 45 +++++++++++-------- .../Formats/Jpg/ZigZagTests.cs | 44 ++++++++++++++++++ 2 files changed, 70 insertions(+), 19 deletions(-) create mode 100644 tests/ImageSharp.Tests/Formats/Jpg/ZigZagTests.cs diff --git a/src/ImageSharp/Formats/Jpeg/Components/ZigZag.cs b/src/ImageSharp/Formats/Jpeg/Components/ZigZag.cs index a3701f2c1b..250e98c02c 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ZigZag.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ZigZag.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -19,30 +19,37 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components /// /// Copy of in a value type /// - public fixed byte Data[64]; + public fixed byte Data[64 + 16]; /// + /// /// Unzig maps from the zigzag ordering to the natural ordering. For example, /// unzig[3] is the column and row of the fourth element in zigzag order. The /// value is 16, which means first column (16%8 == 0) and third row (16/8 == 2). + /// + /// + /// When reading corrupted data, the Huffman decoders could attempt + /// to reference an entry beyond the end of this array (if the decoded + /// zero run length reaches past the end of the block). To prevent + /// wild stores without adding an inner-loop test, we put some extra + /// "63"s after the real entries. This will cause the extra coefficient + /// to be stored in location 63 of the block, not somewhere random. + /// The worst case would be a run-length of 15, which means we need 16 + /// fake entries. + /// /// private static readonly byte[] Unzig = { - 0, - 1, 8, - 16, 9, 2, - 3, 10, 17, 24, - 32, 25, 18, 11, 4, - 5, 12, 19, 26, 33, 40, - 48, 41, 34, 27, 20, 13, 6, - 7, 14, 21, 28, 35, 42, 49, 56, - 57, 50, 43, 36, 29, 22, 15, - 23, 30, 37, 44, 51, 58, - 59, 52, 45, 38, 31, - 39, 46, 53, 60, - 61, 54, 47, - 55, 62, - 63 + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + 63, 63, 63, 63, 63, 63, 63, 63, // Extra entries for safety in decoder + 63, 63, 63, 63, 63, 63, 63, 63 }; /// @@ -68,7 +75,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components { ZigZag result = default; byte* unzigPtr = result.Data; - Marshal.Copy(Unzig, 0, (IntPtr)unzigPtr, 64); + Marshal.Copy(Unzig, 0, (IntPtr)unzigPtr, 64 + 16); return result; } @@ -87,4 +94,4 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components return result; } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Formats/Jpg/ZigZagTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/ZigZagTests.cs new file mode 100644 index 0000000000..e59584991e --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/ZigZagTests.cs @@ -0,0 +1,44 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Formats.Jpeg.Components; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Formats.Jpg +{ + public class ZigZagTests + { + [Fact] + public void ZigZagCanHandleAllPossibleCoefficients() + { + // Mimic the behaviour of the huffman scan decoder using all possible byte values + short[] block = new short[64]; + var zigzag = ZigZag.CreateUnzigTable(); + + for (int h = 0; h < 255; h++) + { + for (int i = 1; i < 64; i++) + { + int s = h; + int r = s >> 4; + s &= 15; + + if (s != 0) + { + i += r; + block[zigzag[i++]] = (short)s; + } + else + { + if (r == 0) + { + break; + } + + i += 16; + } + } + } + } + } +} From 52b30d262acadf1e459a1c5c4c982f022a6197b8 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 4 Jun 2019 20:39:55 +1000 Subject: [PATCH 214/223] Add test image and use constants --- .../Formats/Jpeg/Components/ZigZag.cs | 29 ++++++++++--------- .../Formats/Jpg/JpegDecoderTests.Images.cs | 3 +- tests/ImageSharp.Tests/TestImages.cs | 3 +- .../Issue922-AccessViolationException.jpg | 3 ++ 4 files changed, 22 insertions(+), 16 deletions(-) create mode 100644 tests/Images/Input/Jpg/issues/fuzz/Issue922-AccessViolationException.jpg diff --git a/src/ImageSharp/Formats/Jpeg/Components/ZigZag.cs b/src/ImageSharp/Formats/Jpeg/Components/ZigZag.cs index 250e98c02c..059e2052b3 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ZigZag.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ZigZag.cs @@ -17,17 +17,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components internal unsafe struct ZigZag { /// - /// Copy of in a value type - /// - public fixed byte Data[64 + 16]; - - /// - /// - /// Unzig maps from the zigzag ordering to the natural ordering. For example, - /// unzig[3] is the column and row of the fourth element in zigzag order. The - /// value is 16, which means first column (16%8 == 0) and third row (16/8 == 2). - /// - /// /// When reading corrupted data, the Huffman decoders could attempt /// to reference an entry beyond the end of this array (if the decoded /// zero run length reaches past the end of the block). To prevent @@ -36,9 +25,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components /// to be stored in location 63 of the block, not somewhere random. /// The worst case would be a run-length of 15, which means we need 16 /// fake entries. - /// + /// + private const int Size = 64 + 16; + + /// + /// Copy of in a value type + /// + public fixed byte Data[Size]; + + /// + /// Unzig maps from the zigzag ordering to the natural ordering. For example, + /// unzig[3] is the column and row of the fourth element in zigzag order. The + /// value is 16, which means first column (16%8 == 0) and third row (16/8 == 2). /// private static readonly byte[] Unzig = + new byte[Size] { 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, @@ -75,7 +76,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components { ZigZag result = default; byte* unzigPtr = result.Data; - Marshal.Copy(Unzig, 0, (IntPtr)unzigPtr, 64 + 16); + Marshal.Copy(Unzig, 0, (IntPtr)unzigPtr, Size); return result; } @@ -86,7 +87,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components { Block8x8F result = default; - for (int i = 0; i < 64; i++) + for (int i = 0; i < Block8x8F.Size; i++) { result[Unzig[i]] = qt[i]; } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs index 442fcb3d12..d428000efe 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs @@ -17,6 +17,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg // BUG: The following image has a high difference compared to the expected output: // TestImages.Jpeg.Baseline.Jpeg420Small, + // TestImages.Jpeg.Issues.Fuzz.AccessViolationException922, TestImages.Jpeg.Baseline.Jpeg444, TestImages.Jpeg.Baseline.Bad.BadEOF, @@ -106,4 +107,4 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [TestImages.Jpeg.Progressive.Bad.ExifUndefType] = 0.011f / 100, }; } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 62b7ae2ec0..d8e8719ba1 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System.Linq; @@ -202,6 +202,7 @@ namespace SixLabors.ImageSharp.Tests public const string ArgumentException826C = "Jpg/issues/fuzz/Issue826-ArgumentException-C.jpg"; public const string AccessViolationException827 = "Jpg/issues/fuzz/Issue827-AccessViolationException.jpg"; public const string ExecutionEngineException839 = "Jpg/issues/fuzz/Issue839-ExecutionEngineException.jpg"; + public const string AccessViolationException922 = "Jpg/issues/fuzz/Issue922-AccessViolationException.jpg"; } } diff --git a/tests/Images/Input/Jpg/issues/fuzz/Issue922-AccessViolationException.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue922-AccessViolationException.jpg new file mode 100644 index 0000000000..ef43dc2cfe --- /dev/null +++ b/tests/Images/Input/Jpg/issues/fuzz/Issue922-AccessViolationException.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:52fc6235c184f33d2c817d65f737e2dfe695615ebc79589d8d3d78f6a0af0469 +size 1768097 From 8b7a4dacf0c3f161fadeddcf2ce04e0347a71217 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 5 Jun 2019 00:00:18 +1000 Subject: [PATCH 215/223] Improve robustness of huffman decoder. --- Directory.Build.props | 5 +- .../Components/Decoder/HuffmanScanBuffer.cs | 77 ++++++++++--------- .../Components/Decoder/HuffmanScanDecoder.cs | 24 ++++-- .../Formats/Jpg/JpegDecoderTests.Images.cs | 4 + 4 files changed, 67 insertions(+), 43 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 6e3cc9b3bf..bf004921ea 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -27,9 +27,12 @@ true false true - true + + true + + false diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanBuffer.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanBuffer.cs index 72bfa38646..dd5160414e 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanBuffer.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanBuffer.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System.Runtime.CompilerServices; @@ -19,8 +19,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder // The number of valid bits left to read in the buffer. private int remain; - // Whether there is more data to pull from the stream for the current mcu. - private bool noMore; + // Whether there is no more good data to pull from the stream for the current mcu. + private bool badData; public HuffmanScanBuffer(DoubleBufferedStreamReader stream) { @@ -30,29 +30,29 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder this.Marker = JpegConstants.Markers.XFF; this.MarkerPosition = 0; this.BadMarker = false; - this.noMore = false; - this.Eof = false; + this.badData = false; + this.NoData = false; } /// - /// Gets or sets the current, if any, marker in the input stream. + /// Gets the current, if any, marker in the input stream. /// - public byte Marker { get; set; } + public byte Marker { get; private set; } /// - /// Gets or sets the opening position of an identified marker. + /// Gets the opening position of an identified marker. /// - public long MarkerPosition { get; set; } + public long MarkerPosition { get; private set; } /// - /// Gets or sets a value indicating whether we have a bad marker, I.E. One that is not between RST0 and RST7 + /// Gets a value indicating whether a bad marker has been detected, I.E. One that is not between RST0 and RST7 /// - public bool BadMarker { get; set; } + public bool BadMarker { get; private set; } /// - /// Gets or sets a value indicating whether we have prematurely reached the end of the file. + /// Gets a value indicating whether to continue reading the input stream. /// - public bool Eof { get; set; } + public bool NoData { get; private set; } [MethodImpl(InliningOptions.ShortMethod)] public void CheckBits() @@ -71,8 +71,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder this.Marker = JpegConstants.Markers.XFF; this.MarkerPosition = 0; this.BadMarker = false; - this.noMore = false; - this.Eof = false; + this.badData = false; + this.NoData = false; } [MethodImpl(InliningOptions.ShortMethod)] @@ -141,39 +141,28 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder ulong temp = 0; for (int i = 0; i < 6; i++) { - int b = this.noMore ? 0 : this.stream.ReadByte(); - - if (b == -1) - { - // We've encountered the end of the file stream which means there's no EOI marker in the image - // or the SOS marker has the wrong dimensions set. - this.Eof = true; - b = 0; - } + int b = this.ReadStream(); // Found a marker. if (b == JpegConstants.Markers.XFF) { - this.MarkerPosition = this.stream.Position - 1; - int c = this.stream.ReadByte(); + int c = this.ReadStream(); while (c == JpegConstants.Markers.XFF) { - c = this.stream.ReadByte(); - - if (c == -1) - { - this.Eof = true; - c = 0; - break; - } + // Loop here to discard any padding FF's on terminating marker, + // so that we can save a valid marker value. + c = this.ReadStream(); } + // We accept multiple FF's followed by a 0 as meaning a single FF data byte. + // This data pattern is not valid according to the standard. if (c != 0) { this.Marker = (byte)c; - this.noMore = true; + this.badData = true; if (!this.HasRestart()) { + this.MarkerPosition = this.stream.Position - 2; this.BadMarker = true; } } @@ -184,5 +173,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder return temp; } + + [MethodImpl(InliningOptions.ShortMethod)] + private int ReadStream() + { + int value = this.badData ? 0 : this.stream.ReadByte(); + if (value == -1) + { + // We've encountered the end of the file stream which means there's no EOI marker + // in the image or the SOS marker has the wrong dimensions set. + this.badData = true; + this.NoData = true; + value = 0; + } + + return value; + } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs index 76fea92976..c28733250a 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -130,6 +130,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder int mcu = 0; int mcusPerColumn = this.frame.McusPerColumn; int mcusPerLine = this.frame.McusPerLine; + ref HuffmanScanBuffer buffer = ref this.scanBuffer; // Pre-derive the huffman table to avoid in-loop checks. for (int i = 0; i < this.componentsLength; i++) @@ -171,6 +172,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder for (int x = 0; x < h; x++) { + if (buffer.NoData) + { + return; + } + int blockCol = (mcuCol * h) + x; this.DecodeBlockBaseline( @@ -193,6 +199,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder private unsafe void ParseBaselineDataNonInterleaved() { JpegComponent component = this.components[this.frame.ComponentOrder[0]]; + ref HuffmanScanBuffer buffer = ref this.scanBuffer; int w = component.WidthInBlocks; int h = component.HeightInBlocks; @@ -210,6 +217,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder for (int i = 0; i < w; i++) { + if (buffer.NoData) + { + return; + } + this.DecodeBlockBaseline( component, ref Unsafe.Add(ref blockRef, i), @@ -294,6 +306,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder int mcu = 0; int mcusPerColumn = this.frame.McusPerColumn; int mcusPerLine = this.frame.McusPerLine; + ref HuffmanScanBuffer buffer = ref this.scanBuffer; // Pre-derive the huffman table to avoid in-loop checks. for (int k = 0; k < this.componentsLength; k++) @@ -316,7 +329,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder int order = this.frame.ComponentOrder[k]; JpegComponent component = this.components[order]; ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; - ref HuffmanScanBuffer buffer = ref this.scanBuffer; int h = component.HorizontalSamplingFactor; int v = component.VerticalSamplingFactor; @@ -331,7 +343,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder for (int x = 0; x < h; x++) { - if (buffer.Eof) + if (buffer.NoData) { return; } @@ -375,7 +387,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder for (int i = 0; i < w; i++) { - if (buffer.Eof) + if (buffer.NoData) { return; } @@ -404,7 +416,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder for (int i = 0; i < w; i++) { - if (buffer.Eof) + if (buffer.NoData) { return; } @@ -691,4 +703,4 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder return false; } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs index d428000efe..56496fad1b 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs @@ -17,6 +17,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg // BUG: The following image has a high difference compared to the expected output: // TestImages.Jpeg.Baseline.Jpeg420Small, + + // BUG: While we can decode this image we do not return the same output as libjpeg + // based decoders and are inserting a number of lines equal to the corrupted lines + // below said lines. // TestImages.Jpeg.Issues.Fuzz.AccessViolationException922, TestImages.Jpeg.Baseline.Jpeg444, From 4d3ae3badd8c6c912674067053f39cca66df2375 Mon Sep 17 00:00:00 2001 From: tonygameplan <50851017+tonygameplan@users.noreply.github.com> Date: Tue, 4 Jun 2019 23:44:52 -0700 Subject: [PATCH 216/223] Fix missing "using PixelFormats" line in Readme example (#921) * Fix missing PixelFormats line in first API example "" does not appear to be defined without the "using SixLabors.ImageSharp.PixelFormats;" line and causes a "The type or namespace name 'Rgba32' could not be found (are you missing a using directive or an assembly reference?) (CS0246)" error to occur. * Remove stray newline --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 515fc71969..a5c3457fac 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,7 @@ On platforms supporting netstandard 1.3+ ```csharp using SixLabors.ImageSharp; using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.PixelFormats; // Image.Load(string path) is a shortcut for our default type. // Other pixel formats use Image.Load(string path)) From 30a1b11f898d89722552bee14c2c7855ebcc78c5 Mon Sep 17 00:00:00 2001 From: Brian Popow <38701097+brianpopow@users.noreply.github.com> Date: Mon, 10 Jun 2019 08:24:54 +0200 Subject: [PATCH 217/223] Feature: Bitmap RLE undefined pixel handling (#927) * Add bitmap decoder option, how to treat skipped pixels for RLE * Refactored bitmap tests into smaller tests, instead of just one test which goes through all bitmap files * Add another adobe v3 header bitmap testcase * Using the constant from BmpConstants to Identify bitmaps * Bitmap decoder now can handle oversized palette's * Add test for invalid palette size * Renamed RleUndefinedPixelHandling to RleSkippedPixelHandling * Explicitly using SystemDrawingReferenceDecoder in some BitmapDecoder tests * Add test cases for unsupported bitmaps * Comparing RLE test images to reference decoder only on windows * Add test case for decoding winv4 fast path * Add another 8 Bit RLE test with magick reference decoder * Optimize RLE skipped pixel handling * Refactor RLE decoding to eliminate code duplication * Using MagickReferenceDecoder for the 8-Bit RLE test --- src/ImageSharp/Formats/Bmp/BmpDecoder.cs | 7 +- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 240 ++++++++++++++---- src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs | 9 + .../Formats/Bmp/BmpImageFormatDetector.cs | 6 +- .../Formats/Bmp/IBmpDecoderOptions.cs | 5 +- .../Formats/Bmp/RleSkippedPixelHandling.cs | 26 ++ tests/ImageSharp.Tests/FileTestBase.cs | 2 +- .../Formats/Bmp/BmpDecoderTests.cs | 226 ++++++++++++++++- .../JpegProfilingBenchmarks.cs | 2 +- tests/ImageSharp.Tests/TestImages.cs | 57 +++-- tests/Images/Input/Bmp/invalidPaletteSize.bmp | 3 + tests/Images/Input/Bmp/pal4rlecut.bmp | 3 + tests/Images/Input/Bmp/pal4rletrns.bmp | 3 + tests/Images/Input/Bmp/pal8oversizepal.bmp | 3 + tests/Images/Input/Bmp/pal8rlecut.bmp | 3 + tests/Images/Input/Bmp/pal8rletrns.bmp | 3 + tests/Images/Input/Bmp/rgb24jpeg.bmp | 3 + tests/Images/Input/Bmp/rgb24largepal.bmp | 3 + tests/Images/Input/Bmp/rgb24png.bmp | 3 + tests/Images/Input/Bmp/rgb32h52.bmp | 3 + tests/Images/Input/Bmp/rgba32v4.bmp | 3 + tests/Images/Input/Bmp/rle4-delta-320x240.bmp | 3 + tests/Images/Input/Bmp/rle8-blank-160x120.bmp | 3 + tests/Images/Input/Bmp/rle8-delta-320x240.bmp | 3 + 24 files changed, 537 insertions(+), 85 deletions(-) create mode 100644 src/ImageSharp/Formats/Bmp/RleSkippedPixelHandling.cs create mode 100644 tests/Images/Input/Bmp/invalidPaletteSize.bmp create mode 100644 tests/Images/Input/Bmp/pal4rlecut.bmp create mode 100644 tests/Images/Input/Bmp/pal4rletrns.bmp create mode 100644 tests/Images/Input/Bmp/pal8oversizepal.bmp create mode 100644 tests/Images/Input/Bmp/pal8rlecut.bmp create mode 100644 tests/Images/Input/Bmp/pal8rletrns.bmp create mode 100644 tests/Images/Input/Bmp/rgb24jpeg.bmp create mode 100644 tests/Images/Input/Bmp/rgb24largepal.bmp create mode 100644 tests/Images/Input/Bmp/rgb24png.bmp create mode 100644 tests/Images/Input/Bmp/rgb32h52.bmp create mode 100644 tests/Images/Input/Bmp/rgba32v4.bmp create mode 100644 tests/Images/Input/Bmp/rle4-delta-320x240.bmp create mode 100644 tests/Images/Input/Bmp/rle8-blank-160x120.bmp create mode 100644 tests/Images/Input/Bmp/rle8-delta-320x240.bmp diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs index ebb7ffdf3c..a404ab418b 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs @@ -14,13 +14,18 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// /// JPG /// PNG - /// RLE4 + /// Some OS/2 specific subtypes like: Bitmap Array, Color Icon, Color Pointer, Icon, Pointer. /// /// Formats will be supported in a later releases. We advise always /// to use only 24 Bit Windows bitmaps. /// public sealed class BmpDecoder : IImageDecoder, IBmpDecoderOptions, IImageInfoDetector { + /// + /// Gets or sets a value indicating how to deal with skipped pixels, which can occur during decoding run length encoded bitmaps. + /// + public RleSkippedPixelHandling RleSkippedPixelHandling { get; set; } = RleSkippedPixelHandling.Black; + /// public Image Decode(Configuration configuration, Stream stream) where TPixel : struct, IPixel diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 0cbc4fca1b..294b49ed7e 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -69,7 +69,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp private ImageMetadata metadata; /// - /// The bmp specific metadata. + /// The bitmap specific metadata. /// private BmpMetadata bmpMetadata; @@ -83,10 +83,21 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// private BmpInfoHeader infoHeader; + /// + /// The global configuration. + /// private readonly Configuration configuration; + /// + /// Used for allocating memory during processing operations. + /// private readonly MemoryAllocator memoryAllocator; + /// + /// The bitmap decoder options. + /// + private readonly IBmpDecoderOptions options; + /// /// Initializes a new instance of the class. /// @@ -96,6 +107,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp { this.configuration = configuration; this.memoryAllocator = configuration.MemoryAllocator; + this.options = options; } /// @@ -207,7 +219,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// The image width. /// The pixel component count. /// - /// The . + /// The padding. /// private static int CalculatePadding(int width, int componentCount) { @@ -222,7 +234,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp } /// - /// Decodes a bitmap containing BITFIELDS Compression type. For each color channel, there will be bitmask + /// Decodes a bitmap containing the BITFIELDS Compression type. For each color channel, there will be a bitmask /// which will be used to determine which bits belong to that channel. /// /// The pixel format. @@ -258,8 +270,8 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// /// Looks up color values and builds the image from de-compressed RLE8 or RLE4 data. - /// Compressed RLE8 stream is uncompressed by - /// Compressed RLE4 stream is uncompressed by + /// Compressed RLE8 stream is uncompressed by + /// Compressed RLE4 stream is uncompressed by /// /// The pixel format. /// The compression type. Either RLE4 or RLE8. @@ -273,14 +285,17 @@ namespace SixLabors.ImageSharp.Formats.Bmp { TPixel color = default; using (Buffer2D buffer = this.memoryAllocator.Allocate2D(width, height, AllocationOptions.Clean)) + using (Buffer2D undefinedPixels = this.memoryAllocator.Allocate2D(width, height, AllocationOptions.Clean)) + using (IMemoryOwner rowsWithUndefinedPixels = this.memoryAllocator.Allocate(height, AllocationOptions.Clean)) { + Span rowsWithUndefinedPixelsSpan = rowsWithUndefinedPixels.Memory.Span; if (compression == BmpCompression.RLE8) { - this.UncompressRle8(width, buffer.GetSpan()); + this.UncompressRle8(width, buffer.GetSpan(), undefinedPixels.GetSpan(), rowsWithUndefinedPixelsSpan); } else { - this.UncompressRle4(width, buffer.GetSpan()); + this.UncompressRle4(width, buffer.GetSpan(), undefinedPixels.GetSpan(), rowsWithUndefinedPixelsSpan); } for (int y = 0; y < height; y++) @@ -289,10 +304,46 @@ namespace SixLabors.ImageSharp.Formats.Bmp Span bufferRow = buffer.GetRowSpan(y); Span pixelRow = pixels.GetRowSpan(newY); - for (int x = 0; x < width; x++) + bool rowHasUndefinedPixels = rowsWithUndefinedPixelsSpan[y]; + if (rowHasUndefinedPixels) { - color.FromBgr24(Unsafe.As(ref colors[bufferRow[x] * 4])); - pixelRow[x] = color; + // Slow path with undefined pixels. + for (int x = 0; x < width; x++) + { + byte colorIdx = bufferRow[x]; + if (undefinedPixels[x, y]) + { + switch (this.options.RleSkippedPixelHandling) + { + case RleSkippedPixelHandling.FirstColorOfPalette: + color.FromBgr24(Unsafe.As(ref colors[colorIdx * 4])); + break; + case RleSkippedPixelHandling.Transparent: + color.FromVector4(Vector4.Zero); + break; + + // Default handling for skipped pixels is black (which is what System.Drawing is also doing). + default: + color.FromVector4(new Vector4(0.0f, 0.0f, 0.0f, 1.0f)); + break; + } + } + else + { + color.FromBgr24(Unsafe.As(ref colors[colorIdx * 4])); + } + + pixelRow[x] = color; + } + } + else + { + // Fast path without any undefined pixels. + for (int x = 0; x < width; x++) + { + color.FromBgr24(Unsafe.As(ref colors[bufferRow[x] * 4])); + pixelRow[x] = color; + } } } } @@ -308,7 +359,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// /// The width of the bitmap. /// Buffer for uncompressed data. - private void UncompressRle4(int w, Span buffer) + /// Keeps track over skipped and therefore undefined pixels. + /// Keeps track of rows, which have undefined pixels. + private void UncompressRle4(int w, Span buffer, Span undefinedPixels, Span rowsWithUndefinedPixels) { #if NETCOREAPP2_1 Span cmd = stackalloc byte[2]; @@ -329,21 +382,20 @@ namespace SixLabors.ImageSharp.Formats.Bmp switch (cmd[1]) { case RleEndOfBitmap: + int skipEoB = buffer.Length - count; + RleSkipEndOfBitmap(count, w, skipEoB, undefinedPixels, rowsWithUndefinedPixels); + return; case RleEndOfLine: - int extra = count % w; - if (extra > 0) - { - count += w - extra; - } + count += RleSkipEndOfLine(count, w, undefinedPixels, rowsWithUndefinedPixels); break; case RleDelta: int dx = this.stream.ReadByte(); int dy = this.stream.ReadByte(); - count += (w * dy) + dx; + count += RleSkipDelta(count, w, dx, dy, undefinedPixels, rowsWithUndefinedPixels); break; @@ -374,7 +426,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp } } - // Absolute mode data is aligned to two-byte word-boundary + // Absolute mode data is aligned to two-byte word-boundary. int padding = bytesToRead & 1; this.stream.Skip(padding); @@ -418,7 +470,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// /// The width of the bitmap. /// Buffer for uncompressed data. - private void UncompressRle8(int w, Span buffer) + /// Keeps track of skipped and therefore undefined pixels. + /// Keeps track of rows, which have undefined pixels. + private void UncompressRle8(int w, Span buffer, Span undefinedPixels, Span rowsWithUndefinedPixels) { #if NETCOREAPP2_1 Span cmd = stackalloc byte[2]; @@ -439,27 +493,26 @@ namespace SixLabors.ImageSharp.Formats.Bmp switch (cmd[1]) { case RleEndOfBitmap: + int skipEoB = buffer.Length - count; + RleSkipEndOfBitmap(count, w, skipEoB, undefinedPixels, rowsWithUndefinedPixels); + return; case RleEndOfLine: - int extra = count % w; - if (extra > 0) - { - count += w - extra; - } + count += RleSkipEndOfLine(count, w, undefinedPixels, rowsWithUndefinedPixels); break; case RleDelta: int dx = this.stream.ReadByte(); int dy = this.stream.ReadByte(); - count += (w * dy) + dx; + count += RleSkipDelta(count, w, dx, dy, undefinedPixels, rowsWithUndefinedPixels); break; default: - // If the second byte > 2, we are in 'absolute mode' - // Take this number of bytes from the stream as uncompressed data + // If the second byte > 2, we are in 'absolute mode'. + // Take this number of bytes from the stream as uncompressed data. int length = cmd[1]; byte[] run = new byte[length]; @@ -470,7 +523,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp count += run.Length; - // Absolute mode data is aligned to two-byte word-boundary + // Absolute mode data is aligned to two-byte word-boundary. int padding = length & 1; this.stream.Skip(padding); @@ -481,16 +534,105 @@ namespace SixLabors.ImageSharp.Formats.Bmp else { int max = count + cmd[0]; // as we start at the current count in the following loop, max is count + cmd[0] - byte cmd1 = cmd[1]; // store the value to avoid the repeated indexer access inside the loop + byte colorIdx = cmd[1]; // store the value to avoid the repeated indexer access inside the loop. for (; count < max; count++) { - buffer[count] = cmd1; + buffer[count] = colorIdx; } } } } + /// + /// Keeps track of skipped / undefined pixels, when EndOfBitmap command occurs. + /// + /// The already processed pixel count. + /// The width of the image. + /// The skipped pixel count. + /// The undefined pixels. + /// Rows with undefined pixels. + private static void RleSkipEndOfBitmap( + int count, + int w, + int skipPixelCount, + Span undefinedPixels, + Span rowsWithUndefinedPixels) + { + for (int i = count; i < count + skipPixelCount; i++) + { + undefinedPixels[i] = true; + } + + int skippedRowIdx = count / w; + int skippedRows = (skipPixelCount / w) - 1; + int lastSkippedRow = Math.Min(skippedRowIdx + skippedRows, rowsWithUndefinedPixels.Length - 1); + for (int i = skippedRowIdx; i <= lastSkippedRow; i++) + { + rowsWithUndefinedPixels[i] = true; + } + } + + /// + /// Keeps track of undefined / skipped pixels, when the EndOfLine command occurs. + /// + /// The already processed pixel count. + /// The width of image. + /// The undefined pixels. + /// The rows with undefined pixels. + /// The number of skipped pixels. + private static int RleSkipEndOfLine(int count, int w, Span undefinedPixels, Span rowsWithUndefinedPixels) + { + rowsWithUndefinedPixels[count / w] = true; + int remainingPixelsInRow = count % w; + if (remainingPixelsInRow > 0) + { + int skipEoL = w - remainingPixelsInRow; + for (int i = count; i < count + skipEoL; i++) + { + undefinedPixels[i] = true; + } + + return skipEoL; + } + + return 0; + } + + /// + /// Keeps track of undefined / skipped pixels, when the delta command occurs. + /// + /// The count. + /// The width of the image. + /// Delta skip in x direction. + /// Delta skip in y direction. + /// The undefined pixels. + /// The rows with undefined pixels. + /// The number of skipped pixels. + private static int RleSkipDelta( + int count, + int w, + int dx, + int dy, + Span undefinedPixels, + Span rowsWithUndefinedPixels) + { + int skipDelta = (w * dy) + dx; + for (int i = count; i < count + skipDelta; i++) + { + undefinedPixels[i] = true; + } + + int skippedRowIdx = count / w; + int lastSkippedRow = Math.Min(skippedRowIdx + dy, rowsWithUndefinedPixels.Length - 1); + for (int i = skippedRowIdx; i <= lastSkippedRow; i++) + { + rowsWithUndefinedPixels[i] = true; + } + + return skipDelta; + } + /// /// Reads the color palette from the stream. /// @@ -506,7 +648,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp private void ReadRgbPalette(Buffer2D pixels, byte[] colors, int width, int height, int bitsPerPixel, int bytesPerColorMapEntry, bool inverted) where TPixel : struct, IPixel { - // Pixels per byte (bits per pixel) + // Pixels per byte (bits per pixel). int ppb = 8 / bitsPerPixel; int arrayWidth = (width + ppb - 1) / ppb; @@ -514,7 +656,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp // Bit mask int mask = 0xFF >> (8 - bitsPerPixel); - // Rows are aligned on 4 byte boundaries + // Rows are aligned on 4 byte boundaries. int padding = arrayWidth % 4; if (padding != 0) { @@ -810,11 +952,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp uint maxValueAlpha = 0xFFFFFFFF >> (32 - bitsAlphaMask); float invMaxValueAlpha = 1.0f / maxValueAlpha; - bool unusualBitMask = false; - if (bitsRedMask > 8 || bitsGreenMask > 8 || bitsBlueMask > 8 || invMaxValueAlpha > 8) - { - unusualBitMask = true; - } + bool unusualBitMask = bitsRedMask > 8 || bitsGreenMask > 8 || bitsBlueMask > 8 || invMaxValueAlpha > 8; using (IManagedByteBuffer buffer = this.memoryAllocator.AllocateManagedByteBuffer(stride)) { @@ -910,7 +1048,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp #else byte[] buffer = new byte[BmpInfoHeader.MaxHeaderSize]; #endif - this.stream.Read(buffer, 0, BmpInfoHeader.HeaderSizeSize); // read the header size + + // Read the header size. + this.stream.Read(buffer, 0, BmpInfoHeader.HeaderSizeSize); int headerSize = BinaryPrimitives.ReadInt32LittleEndian(buffer); if (headerSize < BmpInfoHeader.CoreSize) @@ -925,7 +1065,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp headerSize = BmpInfoHeader.MaxHeaderSize; } - // read the rest of the header + // Read the rest of the header. this.stream.Read(buffer, BmpInfoHeader.HeaderSizeSize, headerSize - BmpInfoHeader.HeaderSizeSize); BmpInfoHeaderType infoHeaderType = BmpInfoHeaderType.WinVersion2; @@ -953,7 +1093,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp { byte[] bitfieldsBuffer = new byte[12]; this.stream.Read(bitfieldsBuffer, 0, 12); - Span data = bitfieldsBuffer.AsSpan(); + Span data = bitfieldsBuffer.AsSpan(); this.infoHeader.RedMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4)); this.infoHeader.GreenMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)); this.infoHeader.BlueMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)); @@ -962,7 +1102,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp { byte[] bitfieldsBuffer = new byte[16]; this.stream.Read(bitfieldsBuffer, 0, 16); - Span data = bitfieldsBuffer.AsSpan(); + Span data = bitfieldsBuffer.AsSpan(); this.infoHeader.RedMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4)); this.infoHeader.GreenMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)); this.infoHeader.BlueMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)); @@ -1021,7 +1161,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp this.bmpMetadata = this.metadata.GetFormatMetadata(BmpFormat.Instance); this.bmpMetadata.InfoHeaderType = infoHeaderType; - // We can only encode at these bit rates so far. + // We can only encode at these bit rates so far (1 bit and 4 bit are still missing). if (bitsPerPixel.Equals((short)BmpBitsPerPixel.Pixel8) || bitsPerPixel.Equals((short)BmpBitsPerPixel.Pixel16) || bitsPerPixel.Equals((short)BmpBitsPerPixel.Pixel24) @@ -1030,7 +1170,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp this.bmpMetadata.BitsPerPixel = (BmpBitsPerPixel)bitsPerPixel; } - // skip the remaining header because we can't read those parts + // Skip the remaining header because we can't read those parts. this.stream.Skip(skipAmount); } @@ -1105,10 +1245,12 @@ namespace SixLabors.ImageSharp.Formats.Bmp if (colorMapSize > 0) { - // 256 * 4 - if (colorMapSize > 1024) + // Usually the color palette is 1024 byte (256 colors * 4), but the documentation does not mention a size limit. + // Make sure, that we will not read pass the bitmap offset (starting position of image data). + if ((this.stream.Position + colorMapSize) > this.fileHeader.Offset) { - BmpThrowHelper.ThrowImageFormatException($"Invalid bmp colormap size '{colorMapSize}'"); + BmpThrowHelper.ThrowImageFormatException( + $"Reading the color map would read beyond the bitmap offset. Either the color map size of '{colorMapSize}' is invalid or the bitmap offset."); } palette = new byte[colorMapSize]; @@ -1121,7 +1263,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp int skipAmount = this.fileHeader.Offset - (int)this.stream.Position; if ((skipAmount + (int)this.stream.Position) > this.stream.Length) { - BmpThrowHelper.ThrowImageFormatException($"Invalid fileheader offset found. Offset is greater than the stream length."); + BmpThrowHelper.ThrowImageFormatException("Invalid fileheader offset found. Offset is greater than the stream length."); } if (skipAmount > 0) @@ -1132,4 +1274,4 @@ namespace SixLabors.ImageSharp.Formats.Bmp return bytesPerColorMapEntry; } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index 9fbd0b5adb..4e6ec45021 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -51,10 +51,19 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// private const int ColorPaletteSize8Bit = 1024; + /// + /// Used for allocating memory during processing operations. + /// private readonly MemoryAllocator memoryAllocator; + /// + /// The global configuration. + /// private Configuration configuration; + /// + /// The color depth, in number of bits per pixel. + /// private BmpBitsPerPixel? bitsPerPixel; /// diff --git a/src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs b/src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs index c0814b1dfc..4f862d9295 100644 --- a/src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs +++ b/src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers.Binary; namespace SixLabors.ImageSharp.Formats.Bmp { @@ -21,10 +22,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp private bool IsSupportedFileFormat(ReadOnlySpan header) { - // TODO: This should be in constants - return header.Length >= this.HeaderSize - && header[0] == 0x42 // B - && header[1] == 0x4D; // M + return header.Length >= this.HeaderSize && BinaryPrimitives.ReadInt16LittleEndian(header) == BmpConstants.TypeMarkers.Bitmap; } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs b/src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs index 219d37ca62..f456f2ba3a 100644 --- a/src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs +++ b/src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs @@ -8,6 +8,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// internal interface IBmpDecoderOptions { - // added this for consistency so we can add stuff as required, no options currently available + /// + /// Gets the value indicating how to deal with skipped pixels, which can occur during decoding run length encoded bitmaps. + /// + RleSkippedPixelHandling RleSkippedPixelHandling { get; } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Bmp/RleSkippedPixelHandling.cs b/src/ImageSharp/Formats/Bmp/RleSkippedPixelHandling.cs new file mode 100644 index 0000000000..493fe366ad --- /dev/null +++ b/src/ImageSharp/Formats/Bmp/RleSkippedPixelHandling.cs @@ -0,0 +1,26 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats.Bmp +{ + /// + /// Defines possible options, how skipped pixels during decoding of run length encoded bitmaps should be treated. + /// + public enum RleSkippedPixelHandling : int + { + /// + /// Undefined pixels should be black. This is the default behavior and equal to how System.Drawing handles undefined pixels. + /// + Black = 0, + + /// + /// Undefined pixels should be transparent. + /// + Transparent = 1, + + /// + /// Undefined pixels should have the first color of the palette. + /// + FirstColorOfPalette = 2 + } +} diff --git a/tests/ImageSharp.Tests/FileTestBase.cs b/tests/ImageSharp.Tests/FileTestBase.cs index a056bc474e..4f8475738b 100644 --- a/tests/ImageSharp.Tests/FileTestBase.cs +++ b/tests/ImageSharp.Tests/FileTestBase.cs @@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Tests /// /// A collection of all the bmp test images /// - public static IEnumerable AllBmpFiles = TestImages.Bmp.All; + public static IEnumerable AllBmpFiles = TestImages.Bmp.Benchmark; /// /// A collection of all the jpeg test images diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index e615dbe568..c4dfa724cc 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -1,6 +1,7 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; using System.IO; using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.PixelFormats; @@ -20,27 +21,26 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp { public const PixelTypes CommonNonDefaultPixelTypes = PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.RgbaVector; - public static readonly string[] AllBmpFiles = All; + public static readonly string[] MiscBmpFiles = Miscellaneous; public static readonly string[] BitfieldsBmpFiles = BitFields; public static readonly TheoryData RatioFiles = new TheoryData { - { TestImages.Bmp.Car, 3780, 3780 , PixelResolutionUnit.PixelsPerMeter }, - { TestImages.Bmp.V5Header, 3780, 3780 , PixelResolutionUnit.PixelsPerMeter }, - { TestImages.Bmp.RLE8, 2835, 2835, PixelResolutionUnit.PixelsPerMeter } + { Car, 3780, 3780 , PixelResolutionUnit.PixelsPerMeter }, + { V5Header, 3780, 3780 , PixelResolutionUnit.PixelsPerMeter }, + { RLE8, 2835, 2835, PixelResolutionUnit.PixelsPerMeter } }; [Theory] - [WithFileCollection(nameof(AllBmpFiles), PixelTypes.Rgba32)] - public void DecodeBmp(TestImageProvider provider) + [WithFileCollection(nameof(MiscBmpFiles), PixelTypes.Rgba32)] + public void BmpDecoder_CanDecode_MiscellaneousBitmaps(TestImageProvider provider) where TPixel : struct, IPixel { using (Image image = provider.GetImage(new BmpDecoder())) { image.DebugSave(provider); - if (TestEnvironment.IsWindows) { image.CompareToOriginal(provider); @@ -60,6 +60,174 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp } } + [Theory] + [WithFile(Bit16Inverted, PixelTypes.Rgba32)] + [WithFile(Bit8Inverted, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecode_Inverted(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder())) + { + image.DebugSave(provider); + image.CompareToOriginal(provider); + } + } + + [Theory] + [WithFile(Bit1, PixelTypes.Rgba32)] + [WithFile(Bit1Pal1, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecode_1Bit(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder())) + { + image.DebugSave(provider); + image.CompareToOriginal(provider, new SystemDrawingReferenceDecoder()); + } + } + + [Theory] + [WithFile(Bit4, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecode_4Bit(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder())) + { + image.DebugSave(provider); + // The Magick Reference Decoder can not decode 4-Bit bitmaps, so only execute this on windows. + if (TestEnvironment.IsWindows) + { + image.CompareToOriginal(provider); + } + } + } + + [Theory] + [WithFile(Bit8, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecode_8Bit(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder())) + { + image.DebugSave(provider); + image.CompareToOriginal(provider); + } + } + + [Theory] + [WithFile(Bit16, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecode_16Bit(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder())) + { + image.DebugSave(provider); + image.CompareToOriginal(provider); + } + } + + [Theory] + [WithFile(Bit32Rgb, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecode_32Bit(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder())) + { + image.DebugSave(provider); + image.CompareToOriginal(provider); + } + } + + [Theory] + [WithFile(Rgba32v4, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecode_32BitV4Header_Fast(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder())) + { + image.DebugSave(provider); + image.CompareToOriginal(provider); + } + } + + [Theory] + [WithFile(RLE4Cut, PixelTypes.Rgba32)] + [WithFile(RLE4Delta, PixelTypes.Rgba32)] + [WithFile(Rle4Delta320240, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecode_RunLengthEncoded_4Bit_WithDelta(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder() { RleSkippedPixelHandling = RleSkippedPixelHandling.Black })) + { + image.DebugSave(provider); + // The Magick Reference Decoder can not decode 4-Bit bitmaps, so only execute this on windows. + if (TestEnvironment.IsWindows) + { + image.CompareToOriginal(provider); + } + } + } + + [Theory] + [WithFile(RLE4, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecode_RunLengthEncoded_4Bit(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder() { RleSkippedPixelHandling = RleSkippedPixelHandling.Black })) + { + image.DebugSave(provider); + // The Magick Reference Decoder can not decode 4-Bit bitmaps, so only execute this on windows. + if (TestEnvironment.IsWindows) + { + image.CompareToOriginal(provider); + } + } + } + + [Theory] + [WithFile(RLE8Cut, PixelTypes.Rgba32)] + [WithFile(RLE8Delta, PixelTypes.Rgba32)] + [WithFile(Rle8Delta320240, PixelTypes.Rgba32)] + [WithFile(Rle8Blank160120, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecode_RunLengthEncoded_8Bit_WithDelta_SystemDrawingRefDecoder(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder() { RleSkippedPixelHandling = RleSkippedPixelHandling.Black })) + { + image.DebugSave(provider); + if (TestEnvironment.IsWindows) + { + image.CompareToOriginal(provider, new SystemDrawingReferenceDecoder()); + } + } + } + + [Theory] + [WithFile(RLE8Cut, PixelTypes.Rgba32)] + [WithFile(RLE8Delta, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecode_RunLengthEncoded_8Bit_WithDelta_MagickRefDecoder(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder() { RleSkippedPixelHandling = RleSkippedPixelHandling.FirstColorOfPalette })) + { + image.DebugSave(provider); + image.CompareToOriginal(provider, new MagickReferenceDecoder()); + } + } + + [Theory] + [WithFile(RLE8, PixelTypes.Rgba32)] + [WithFile(RLE8Inverted, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecode_RunLengthEncoded_8Bit(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder() { RleSkippedPixelHandling = RleSkippedPixelHandling.FirstColorOfPalette })) + { + image.DebugSave(provider); + image.CompareToOriginal(provider, new MagickReferenceDecoder()); + } + } + [Theory] [WithFile(RgbaAlphaBitfields, PixelTypes.Rgba32)] public void BmpDecoder_CanDecodeAlphaBitfields(TestImageProvider provider) @@ -106,6 +274,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp [Theory] [WithFile(WinBmpv2, PixelTypes.Rgba32)] + [WithFile(CoreHeader, PixelTypes.Rgba32)] public void BmpDecoder_CanDecodeBmpv2(TestImageProvider provider) where TPixel : struct, IPixel { @@ -141,7 +310,41 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp } [Theory] - [WithFile(Rgba32bf56, PixelTypes.Rgba32)] + [WithFile(OversizedPalette, PixelTypes.Rgba32)] + [WithFile(Rgb24LargePalette, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecodeOversizedPalette(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder())) + { + image.DebugSave(provider); + if (TestEnvironment.IsWindows) + { + image.CompareToOriginal(provider); + } + } + } + + [Theory] + [WithFile(InvalidPaletteSize, PixelTypes.Rgba32)] + public void BmpDecoder_ThrowsImageFormatException_OnInvalidPaletteSize(TestImageProvider provider) + where TPixel : struct, IPixel + { + Assert.Throws( () => { using (Image image = provider.GetImage(new BmpDecoder())) { } }); + } + + [Theory] + [WithFile(Rgb24jpeg, PixelTypes.Rgba32)] + [WithFile(Rgb24png, PixelTypes.Rgba32)] + public void BmpDecoder_ThrowsNotSupportedException_OnUnsupportedBitmaps(TestImageProvider provider) + where TPixel : struct, IPixel + { + Assert.Throws(() => { using (Image image = provider.GetImage(new BmpDecoder())) { } }); + } + + [Theory] + [WithFile(Rgba32bf56AdobeV3, PixelTypes.Rgba32)] + [WithFile(Rgb32h52AdobeV3, PixelTypes.Rgba32)] public void BmpDecoder_CanDecodeAdobeBmpv3(TestImageProvider provider) where TPixel : struct, IPixel { @@ -166,6 +369,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp [Theory] [WithFile(WinBmpv5, PixelTypes.Rgba32)] + [WithFile(V5Header, PixelTypes.Rgba32)] public void BmpDecoder_CanDecodeBmpv5(TestImageProvider provider) where TPixel : struct, IPixel { @@ -223,6 +427,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp [InlineData(Bit8, 8)] [InlineData(Bit8Inverted, 8)] [InlineData(Bit4, 4)] + [InlineData(Bit1, 1)] + [InlineData(Bit1Pal1, 1)] public void Identify(string imagePath, int expectedPixelSize) { var testFile = TestFile.Create(imagePath); @@ -281,4 +487,4 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp } } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/ProfilingBenchmarks/JpegProfilingBenchmarks.cs b/tests/ImageSharp.Tests/ProfilingBenchmarks/JpegProfilingBenchmarks.cs index 65989556d2..95a47fd7cc 100644 --- a/tests/ImageSharp.Tests/ProfilingBenchmarks/JpegProfilingBenchmarks.cs +++ b/tests/ImageSharp.Tests/ProfilingBenchmarks/JpegProfilingBenchmarks.cs @@ -82,7 +82,7 @@ namespace SixLabors.ImageSharp.Tests.ProfilingBenchmarks return; } - string[] testFiles = TestImages.Bmp.All + string[] testFiles = TestImages.Bmp.Benchmark .Concat(new[] { TestImages.Jpeg.Baseline.Calliphora, TestImages.Jpeg.Baseline.Cmyk }).ToArray(); Image[] testImages = testFiles.Select( diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index d8e8719ba1..d041f48544 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -231,8 +231,15 @@ namespace SixLabors.ImageSharp.Tests public const string CoreHeader = "Bmp/BitmapCoreHeaderQR.bmp"; public const string V5Header = "Bmp/BITMAPV5HEADER.bmp"; public const string RLE8 = "Bmp/RunLengthEncoded.bmp"; + public const string RLE8Cut = "Bmp/pal8rlecut.bmp"; + public const string RLE8Delta = "Bmp/pal8rletrns.bmp"; + public const string Rle8Delta320240 = "Bmp/rle8-delta-320x240.bmp"; + public const string Rle8Blank160120 = "Bmp/rle8-blank-160x120.bmp"; + public const string RLE8Inverted = "Bmp/RunLengthEncoded-inverted.bmp"; public const string RLE4 = "Bmp/pal4rle.bmp"; - public const string RLEInverted = "Bmp/RunLengthEncoded-inverted.bmp"; + public const string RLE4Cut = "Bmp/pal4rlecut.bmp"; + public const string RLE4Delta = "Bmp/pal4rletrns.bmp"; + public const string Rle4Delta320240 = "Bmp/rle4-delta-320x240.bmp"; public const string Bit1 = "Bmp/pal1.bmp"; public const string Bit1Pal1 = "Bmp/pal1p1.bmp"; public const string Bit4 = "Bmp/pal4.bmp"; @@ -256,15 +263,22 @@ namespace SixLabors.ImageSharp.Tests public const string Os2v2 = "Bmp/pal8os2v2.bmp"; public const string LessThanFullSizedPalette = "Bmp/pal8os2sp.bmp"; public const string Pal8Offset = "Bmp/pal8offs.bmp"; + public const string OversizedPalette = "Bmp/pal8oversizepal.bmp"; + public const string Rgb24LargePalette = "Bmp/rgb24largepal.bmp"; + public const string InvalidPaletteSize = "Bmp/invalidPaletteSize.bmp"; + public const string Rgb24jpeg = "Bmp/rgb24jpeg.bmp"; + public const string Rgb24png = "Bmp/rgb24png.bmp"; + public const string Rgba32v4 = "Bmp/rgba32v4.bmp"; - // Bitmap images with compression type BITFIELDS + // Bitmap images with compression type BITFIELDS. public const string Rgb32bfdef = "Bmp/rgb32bfdef.bmp"; public const string Rgb32bf = "Bmp/rgb32bf.bmp"; public const string Rgb16bfdef = "Bmp/rgb16bfdef.bmp"; public const string Rgb16565 = "Bmp/rgb16-565.bmp"; public const string Rgb16565pal = "Bmp/rgb16-565pal.bmp"; public const string Issue735 = "Bmp/issue735.bmp"; - public const string Rgba32bf56 = "Bmp/rgba32h56.bmp"; + public const string Rgba32bf56AdobeV3 = "Bmp/rgba32h56.bmp"; + public const string Rgb32h52AdobeV3 = "Bmp/rgb32h52.bmp"; public const string Rgba321010102 = "Bmp/rgba32-1010102.bmp"; public const string RgbaAlphaBitfields = "Bmp/rgba32abf.bmp"; @@ -278,25 +292,32 @@ namespace SixLabors.ImageSharp.Tests Issue735, }; - public static readonly string[] All + public static readonly string[] Miscellaneous = { Car, F, - NegHeight, - CoreHeader, - V5Header, - RLE4, - RLE8, - RLEInverted, - Bit1, - Bit1Pal1, - Bit4, - Bit8, - Bit8Inverted, - Bit16, - Bit16Inverted, - Bit32Rgb + NegHeight }; + + public static readonly string[] Benchmark + = { + Car, + F, + NegHeight, + CoreHeader, + V5Header, + RLE4, + RLE8, + RLE8Inverted, + Bit1, + Bit1Pal1, + Bit4, + Bit8, + Bit8Inverted, + Bit16, + Bit16Inverted, + Bit32Rgb + }; } public static class Gif diff --git a/tests/Images/Input/Bmp/invalidPaletteSize.bmp b/tests/Images/Input/Bmp/invalidPaletteSize.bmp new file mode 100644 index 0000000000..8a4b231166 --- /dev/null +++ b/tests/Images/Input/Bmp/invalidPaletteSize.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eaea78be2e8e5579e2469400b8d811b125be805459888c3bd390570d21ffeab8 +size 9270 diff --git a/tests/Images/Input/Bmp/pal4rlecut.bmp b/tests/Images/Input/Bmp/pal4rlecut.bmp new file mode 100644 index 0000000000..f67004039b --- /dev/null +++ b/tests/Images/Input/Bmp/pal4rlecut.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e3dcbe1f39144845f339a88c9cbf34adc3e0b355440dbcb13e987aec77bb2137 +size 3610 diff --git a/tests/Images/Input/Bmp/pal4rletrns.bmp b/tests/Images/Input/Bmp/pal4rletrns.bmp new file mode 100644 index 0000000000..674abdaff8 --- /dev/null +++ b/tests/Images/Input/Bmp/pal4rletrns.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2fcbaa0f387c57ba678ced91dfb2db5b2e544e2ed28a7875459e551b514daf84 +size 4326 diff --git a/tests/Images/Input/Bmp/pal8oversizepal.bmp b/tests/Images/Input/Bmp/pal8oversizepal.bmp new file mode 100644 index 0000000000..e80319df9a --- /dev/null +++ b/tests/Images/Input/Bmp/pal8oversizepal.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:51c89919fc6b85cc11850054ff16bb947c11dead620e35a92e46d7b0fde79faf +size 9446 diff --git a/tests/Images/Input/Bmp/pal8rlecut.bmp b/tests/Images/Input/Bmp/pal8rlecut.bmp new file mode 100644 index 0000000000..b3e46321cc --- /dev/null +++ b/tests/Images/Input/Bmp/pal8rlecut.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:726c092da72e5412d2e1a0e3d9e35ee3630869e368fd4534c631f53bb5608a11 +size 7980 diff --git a/tests/Images/Input/Bmp/pal8rletrns.bmp b/tests/Images/Input/Bmp/pal8rletrns.bmp new file mode 100644 index 0000000000..22f5629186 --- /dev/null +++ b/tests/Images/Input/Bmp/pal8rletrns.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:445b856331c5d03054887d6555f56a8882f0aabbe0d59e322d9ff0be7f0eee94 +size 9212 diff --git a/tests/Images/Input/Bmp/rgb24jpeg.bmp b/tests/Images/Input/Bmp/rgb24jpeg.bmp new file mode 100644 index 0000000000..6f5ae3b56c --- /dev/null +++ b/tests/Images/Input/Bmp/rgb24jpeg.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c09aad5b4717408e42d7cdd3d51d884d20be6a69adf73c7afcc47201df051f30 +size 2457 diff --git a/tests/Images/Input/Bmp/rgb24largepal.bmp b/tests/Images/Input/Bmp/rgb24largepal.bmp new file mode 100644 index 0000000000..ac46d03da8 --- /dev/null +++ b/tests/Images/Input/Bmp/rgb24largepal.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:62976c8077dddd97b7de7396d70d6aaded717c387a5272f3f6e18bb4abcd5f45 +size 25830 diff --git a/tests/Images/Input/Bmp/rgb24png.bmp b/tests/Images/Input/Bmp/rgb24png.bmp new file mode 100644 index 0000000000..40969196e7 --- /dev/null +++ b/tests/Images/Input/Bmp/rgb24png.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:07dcf8ac7fceee50ba5c3d89ece956e14b9bb4ea05f0a2b75f6958150d3d7a03 +size 1210 diff --git a/tests/Images/Input/Bmp/rgb32h52.bmp b/tests/Images/Input/Bmp/rgb32h52.bmp new file mode 100644 index 0000000000..114c03fbd7 --- /dev/null +++ b/tests/Images/Input/Bmp/rgb32h52.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9baf23d23b75ad2a641decce0e5b59640b3359ff327eecfa1b888595a6b502d6 +size 32578 diff --git a/tests/Images/Input/Bmp/rgba32v4.bmp b/tests/Images/Input/Bmp/rgba32v4.bmp new file mode 100644 index 0000000000..6f9bf07bf8 --- /dev/null +++ b/tests/Images/Input/Bmp/rgba32v4.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c932d7241122b221ab8f35c427082643ea0241493276c5aef0e64a49b8b55b6c +size 32634 diff --git a/tests/Images/Input/Bmp/rle4-delta-320x240.bmp b/tests/Images/Input/Bmp/rle4-delta-320x240.bmp new file mode 100644 index 0000000000..a52aad3d89 --- /dev/null +++ b/tests/Images/Input/Bmp/rle4-delta-320x240.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:514d8bafa663017276ce0a91eb90025bd5e0296984c50f0da2f477cd27ff6d68 +size 3686 diff --git a/tests/Images/Input/Bmp/rle8-blank-160x120.bmp b/tests/Images/Input/Bmp/rle8-blank-160x120.bmp new file mode 100644 index 0000000000..d1d4496073 --- /dev/null +++ b/tests/Images/Input/Bmp/rle8-blank-160x120.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ffb99a2ee179388c1e379492fee83e67d600fbf740fbcfb0a4cf8e4d42a662b3 +size 1080 diff --git a/tests/Images/Input/Bmp/rle8-delta-320x240.bmp b/tests/Images/Input/Bmp/rle8-delta-320x240.bmp new file mode 100644 index 0000000000..ff8ee7a303 --- /dev/null +++ b/tests/Images/Input/Bmp/rle8-delta-320x240.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:522a154731ea38e4ee29b4f27672b7d881bcfb1f0954fae126e79a27a63d5f06 +size 4646 From 34666d724b318a61a0e5e02c0a9198a7462e90ea Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 14 Jun 2019 12:23:43 +1000 Subject: [PATCH 218/223] Fix 925 (#929) * Prevent overflow * Cleanup huffman table * Search for RST markers. * Fix Benchmarks project --- .../Components/Decoder/HuffmanScanBuffer.cs | 92 +++++++++++++------ .../Components/Decoder/HuffmanScanDecoder.cs | 14 ++- .../Jpeg/Components/Decoder/HuffmanTable.cs | 48 +++++----- .../Formats/Jpeg/JpegDecoderCore.cs | 6 +- .../ImageSharp.Benchmarks.csproj | 11 +-- .../Formats/Jpg/JpegDecoderTests.Images.cs | 8 +- tests/Images/External | 2 +- 7 files changed, 103 insertions(+), 78 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanBuffer.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanBuffer.cs index dd5160414e..13c89c82cf 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanBuffer.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanBuffer.cs @@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder private ulong data; // The number of valid bits left to read in the buffer. - private int remain; + private int remainingBits; // Whether there is no more good data to pull from the stream for the current mcu. private bool badData; @@ -26,10 +26,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { this.stream = stream; this.data = 0ul; - this.remain = 0; + this.remainingBits = 0; this.Marker = JpegConstants.Markers.XFF; this.MarkerPosition = 0; - this.BadMarker = false; this.badData = false; this.NoData = false; } @@ -44,11 +43,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// public long MarkerPosition { get; private set; } - /// - /// Gets a value indicating whether a bad marker has been detected, I.E. One that is not between RST0 and RST7 - /// - public bool BadMarker { get; private set; } - /// /// Gets a value indicating whether to continue reading the input stream. /// @@ -57,7 +51,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder [MethodImpl(InliningOptions.ShortMethod)] public void CheckBits() { - if (this.remain < 16) + if (this.remainingBits < 16) { this.FillBuffer(); } @@ -67,27 +61,31 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder public void Reset() { this.data = 0ul; - this.remain = 0; + this.remainingBits = 0; this.Marker = JpegConstants.Markers.XFF; this.MarkerPosition = 0; - this.BadMarker = false; this.badData = false; this.NoData = false; } + /// + /// Whether a RST marker has been detected, I.E. One that is between RST0 and RST7 + /// [MethodImpl(InliningOptions.ShortMethod)] - public bool HasRestart() - { - byte m = this.Marker; - return m >= JpegConstants.Markers.RST0 && m <= JpegConstants.Markers.RST7; - } + public bool HasRestartMarker() => HasRestart(this.Marker); + + /// + /// Whether a bad marker has been detected, I.E. One that is not between RST0 and RST7 + /// + [MethodImpl(InliningOptions.ShortMethod)] + public bool HasBadMarker() => this.Marker != JpegConstants.Markers.XFF && !this.HasRestartMarker(); [MethodImpl(InliningOptions.ShortMethod)] public void FillBuffer() { // Attempt to load at least the minimum number of required bits into the buffer. // We fail to do so only if we hit a marker or reach the end of the input stream. - this.remain += 48; + this.remainingBits += 48; this.data = (this.data << 48) | this.GetBytes(); } @@ -101,17 +99,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder if (size == JpegConstants.Huffman.SlowBits) { - ulong x = this.data << (JpegConstants.Huffman.RegisterSize - this.remain); + ulong x = this.data << (JpegConstants.Huffman.RegisterSize - this.remainingBits); while (x > h.MaxCode[size]) { size++; } v = (int)(x >> (JpegConstants.Huffman.RegisterSize - size)); - symbol = h.Values[h.ValOffset[size] + v]; + symbol = h.Values[(h.ValOffset[size] + v) & 0xFF]; } - this.remain -= size; + this.remainingBits -= size; return symbol; } @@ -124,10 +122,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder } [MethodImpl(InliningOptions.ShortMethod)] - public int GetBits(int nbits) => (int)ExtractBits(this.data, this.remain -= nbits, nbits); + private static bool HasRestart(byte marker) + => marker >= JpegConstants.Markers.RST0 && marker <= JpegConstants.Markers.RST7; [MethodImpl(InliningOptions.ShortMethod)] - public int PeekBits(int nbits) => (int)ExtractBits(this.data, this.remain - nbits, nbits); + public int GetBits(int nbits) => (int)ExtractBits(this.data, this.remainingBits -= nbits, nbits); + + [MethodImpl(InliningOptions.ShortMethod)] + public int PeekBits(int nbits) => (int)ExtractBits(this.data, this.remainingBits - nbits, nbits); [MethodImpl(InliningOptions.ShortMethod)] private static ulong ExtractBits(ulong value, int offset, int size) => (value >> offset) & (ulong)((1 << size) - 1); @@ -149,22 +151,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder int c = this.ReadStream(); while (c == JpegConstants.Markers.XFF) { - // Loop here to discard any padding FF's on terminating marker, + // Loop here to discard any padding FF bytes on terminating marker, // so that we can save a valid marker value. c = this.ReadStream(); } - // We accept multiple FF's followed by a 0 as meaning a single FF data byte. + // We accept multiple FF bytes followed by a 0 as meaning a single FF data byte. // This data pattern is not valid according to the standard. if (c != 0) { this.Marker = (byte)c; this.badData = true; - if (!this.HasRestart()) - { - this.MarkerPosition = this.stream.Position - 2; - this.BadMarker = true; - } + this.MarkerPosition = this.stream.Position - 2; } } @@ -174,6 +172,42 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder return temp; } + [MethodImpl(InliningOptions.ShortMethod)] + public bool FindNextMarker() + { + while (true) + { + int b = this.stream.ReadByte(); + if (b == -1) + { + return false; + } + + // Found a marker. + if (b == JpegConstants.Markers.XFF) + { + while (b == JpegConstants.Markers.XFF) + { + // Loop here to discard any padding FF bytes on terminating marker. + b = this.stream.ReadByte(); + if (b == -1) + { + return false; + } + } + + // Found a valid marker. Exit loop + if (b != 0) + { + this.Marker = (byte)b; + this.badData = true; + this.MarkerPosition = this.stream.Position - 2; + return true; + } + } + } + } + [MethodImpl(InliningOptions.ShortMethod)] private int ReadStream() { diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs index c28733250a..c50812e253 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs @@ -106,7 +106,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder this.ParseProgressiveData(); } - if (this.scanBuffer.BadMarker) + if (this.scanBuffer.HasBadMarker()) { this.stream.Position = this.scanBuffer.MarkerPosition; } @@ -684,15 +684,23 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { if (this.restartInterval > 0 && (--this.todo) == 0) { + if (this.scanBuffer.Marker == JpegConstants.Markers.XFF) + { + if (!this.scanBuffer.FindNextMarker()) + { + return false; + } + } + this.todo = this.restartInterval; - if (this.scanBuffer.HasRestart()) + if (this.scanBuffer.HasRestartMarker()) { this.Reset(); return true; } - if (this.scanBuffer.Marker != JpegConstants.Markers.XFF) + if (this.scanBuffer.HasBadMarker()) { this.stream.Position = this.scanBuffer.MarkerPosition; this.Reset(); diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs index 21ed5018f3..4685ba2895 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs @@ -1,20 +1,18 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.Memory; namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { /// - /// Represents a Huffman coding table containing basic coding data plus tables for accellerated computation. + /// Represents a Huffman coding table containing basic coding data plus tables for accelerated computation. /// [StructLayout(LayoutKind.Sequential)] internal unsafe struct HuffmanTable { - private readonly MemoryAllocator memoryAllocator; private bool isConfigured; /// @@ -60,13 +58,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// /// Initializes a new instance of the struct. /// - /// The to use for buffer allocations. /// The code lengths /// The huffman values - public HuffmanTable(MemoryAllocator memoryAllocator, ReadOnlySpan codeLengths, ReadOnlySpan values) + public HuffmanTable(ReadOnlySpan codeLengths, ReadOnlySpan values) { this.isConfigured = false; - this.memoryAllocator = memoryAllocator; Unsafe.CopyBlockUnaligned(ref this.Sizes[0], ref MemoryMarshal.GetReference(codeLengths), (uint)codeLengths.Length); Unsafe.CopyBlockUnaligned(ref this.Values[0], ref MemoryMarshal.GetReference(values), (uint)values.Length); } @@ -81,33 +77,31 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder return; } - int p, si; - Span huffsize = stackalloc char[257]; - Span huffcode = stackalloc uint[257]; - uint code; + Span huffSize = stackalloc char[257]; + Span huffCode = stackalloc uint[257]; // Figure C.1: make table of Huffman code length for each symbol - p = 0; + int p = 0; for (int l = 1; l <= 16; l++) { int i = this.Sizes[l]; while (i-- != 0) { - huffsize[p++] = (char)l; + huffSize[p++] = (char)l; } } - huffsize[p] = (char)0; + huffSize[p] = (char)0; // Figure C.2: generate the codes themselves - code = 0; - si = huffsize[0]; + uint code = 0; + int si = huffSize[0]; p = 0; - while (huffsize[p] != 0) + while (huffSize[p] != 0) { - while (huffsize[p] == si) + while (huffSize[p] == si) { - huffcode[p++] = code; + huffCode[p++] = code; code++; } @@ -121,10 +115,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { if (this.Sizes[l] != 0) { - int offset = p - (int)huffcode[p]; + int offset = p - (int)huffCode[p]; this.ValOffset[l] = offset; p += this.Sizes[l]; - this.MaxCode[l] = huffcode[p - 1]; // Maximum code of length l + this.MaxCode[l] = huffCode[p - 1]; // Maximum code of length l this.MaxCode[l] <<= 64 - l; // Left justify this.MaxCode[l] |= (1ul << (64 - l)) - 1; } @@ -150,14 +144,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { for (int i = 1; i <= this.Sizes[length]; i++, p++) { - // length = current code's length, p = its index in huffcode[] & huffval[]. + // length = current code's length, p = its index in huffCode[] & Values[]. // Generate left-justified code followed by all possible bit sequences - int lookbits = (int)(huffcode[p] << (JpegConstants.Huffman.LookupBits - length)); + int lookBits = (int)(huffCode[p] << (JpegConstants.Huffman.LookupBits - length)); for (int ctr = 1 << (JpegConstants.Huffman.LookupBits - length); ctr > 0; ctr--) { - this.LookaheadSize[lookbits] = (byte)length; - this.LookaheadValue[lookbits] = this.Values[p]; - lookbits++; + this.LookaheadSize[lookBits] = (byte)length; + this.LookaheadValue[lookBits] = this.Values[p]; + lookBits++; } } } @@ -165,4 +159,4 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder this.isConfigured = true; } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index fd0c289b88..bf7c8f9c88 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -952,7 +952,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// The values [MethodImpl(InliningOptions.ShortMethod)] private void BuildHuffmanTable(HuffmanTable[] tables, int index, ReadOnlySpan codeLengths, ReadOnlySpan values) - => tables[index] = new HuffmanTable(this.configuration.MemoryAllocator, codeLengths, values); + => tables[index] = new HuffmanTable(codeLengths, values); /// /// Reads a from the stream advancing it by two bytes @@ -992,4 +992,4 @@ namespace SixLabors.ImageSharp.Formats.Jpeg return image; } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj index 1e951f5d0e..14ad5635cd 100644 --- a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj +++ b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj @@ -6,11 +6,8 @@ Exe SixLabors.ImageSharp.Benchmarks netcoreapp2.1;net472 - - - - win7-x64 - false + false + false @@ -18,10 +15,6 @@ - - - - diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs index 56496fad1b..4a3ef9b956 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs @@ -15,14 +15,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg TestImages.Jpeg.Baseline.Jpeg400, TestImages.Jpeg.Baseline.Testorig420, - // BUG: The following image has a high difference compared to the expected output: + // BUG: The following image has a high difference compared to the expected output: 1.0096% // TestImages.Jpeg.Baseline.Jpeg420Small, - // BUG: While we can decode this image we do not return the same output as libjpeg - // based decoders and are inserting a number of lines equal to the corrupted lines - // below said lines. - // TestImages.Jpeg.Issues.Fuzz.AccessViolationException922, - + TestImages.Jpeg.Issues.Fuzz.AccessViolationException922, TestImages.Jpeg.Baseline.Jpeg444, TestImages.Jpeg.Baseline.Bad.BadEOF, TestImages.Jpeg.Baseline.MultiScanBaselineCMYK, diff --git a/tests/Images/External b/tests/Images/External index 42b3b980ed..acc32594c1 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 42b3b980ed07afd7b6603a5bfa6ffb91d6c8a124 +Subproject commit acc32594c125656840f8a17e69b0ebb49a370fa6 From cf61a08b31805263f44ce806d85509c1ad6911ba Mon Sep 17 00:00:00 2001 From: Brian Popow <38701097+brianpopow@users.noreply.github.com> Date: Fri, 14 Jun 2019 15:36:28 +0200 Subject: [PATCH 219/223] Bitmap decoder now can decode bitmap arrays (#930) * Bitmap Decoder can now decode BitmapArray * Add tests for bitmap metadata decoing. Fix an issue that a bitmap with a v5 header would be set in the metadata as an v4 header. * Fixed issue with decoding bitmap arrays: color map size was not determined correctly. Added more test images. * Refactor colormap size duplicate declaration. * Fixed an issue, that when an unsupported bitmap is loaded the typ marker was not correctly shown in the error message --- .../Formats/Bmp/BmpArrayFileHeader.cs | 53 ++++++++++++++ src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 69 ++++++++++++------- src/ImageSharp/Formats/Bmp/BmpFileHeader.cs | 6 +- .../Formats/Bmp/BmpImageFormatDetector.cs | 8 ++- src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs | 17 +++-- .../Formats/Bmp/BmpDecoderTests.cs | 64 +++++++++++++++-- .../Formats/Bmp/BmpEncoderTests.cs | 6 +- .../Formats/Bmp/BmpMetaDataTests.cs | 32 ++++++++- tests/ImageSharp.Tests/TestImages.cs | 8 +++ tests/Images/Input/Bmp/9S.BMP | 3 + tests/Images/Input/Bmp/DIAMOND.BMP | 3 + tests/Images/Input/Bmp/GMARBLE.BMP | 3 + tests/Images/Input/Bmp/PINES.BMP | 3 + tests/Images/Input/Bmp/SKATER.BMP | 3 + tests/Images/Input/Bmp/SPADE.BMP | 3 + tests/Images/Input/Bmp/SUNFLOW.BMP | 3 + tests/Images/Input/Bmp/WARPD.BMP | 3 + 17 files changed, 240 insertions(+), 47 deletions(-) create mode 100644 src/ImageSharp/Formats/Bmp/BmpArrayFileHeader.cs create mode 100644 tests/Images/Input/Bmp/9S.BMP create mode 100644 tests/Images/Input/Bmp/DIAMOND.BMP create mode 100644 tests/Images/Input/Bmp/GMARBLE.BMP create mode 100644 tests/Images/Input/Bmp/PINES.BMP create mode 100644 tests/Images/Input/Bmp/SKATER.BMP create mode 100644 tests/Images/Input/Bmp/SPADE.BMP create mode 100644 tests/Images/Input/Bmp/SUNFLOW.BMP create mode 100644 tests/Images/Input/Bmp/WARPD.BMP diff --git a/src/ImageSharp/Formats/Bmp/BmpArrayFileHeader.cs b/src/ImageSharp/Formats/Bmp/BmpArrayFileHeader.cs new file mode 100644 index 0000000000..e8afb422a8 --- /dev/null +++ b/src/ImageSharp/Formats/Bmp/BmpArrayFileHeader.cs @@ -0,0 +1,53 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp.Formats.Bmp +{ + [StructLayout(LayoutKind.Sequential, Pack = 1)] + internal readonly struct BmpArrayFileHeader + { + public BmpArrayFileHeader(short type, int size, int offsetToNext, short width, short height) + { + this.Type = type; + this.Size = size; + this.OffsetToNext = offsetToNext; + this.ScreenWidth = width; + this.ScreenHeight = height; + } + + /// + /// Gets the Bitmap identifier. + /// The field used to identify the bitmap file: 0x42 0x41 (Hex code points for B and A). + /// + public short Type { get; } + + /// + /// Gets the size of this header. + /// + public int Size { get; } + + /// + /// Gets the offset to next OS2BMPARRAYFILEHEADER. + /// This offset is calculated from the starting byte of the file. A value of zero indicates that this header is for the last image in the array list. + /// + public int OffsetToNext { get; } + + /// + /// Gets the width of the image display in pixels. + /// + public short ScreenWidth { get; } + + /// + /// Gets the height of the image display in pixels. + /// + public short ScreenHeight { get; } + + public static BmpArrayFileHeader Parse(Span data) + { + return MemoryMarshal.Cast(data)[0]; + } + } +} diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 294b49ed7e..1ceb35283a 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -1053,18 +1053,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp this.stream.Read(buffer, 0, BmpInfoHeader.HeaderSizeSize); int headerSize = BinaryPrimitives.ReadInt32LittleEndian(buffer); - if (headerSize < BmpInfoHeader.CoreSize) + if (headerSize < BmpInfoHeader.CoreSize || headerSize > BmpInfoHeader.MaxHeaderSize) { BmpThrowHelper.ThrowNotSupportedException($"ImageSharp does not support this BMP file. HeaderSize is '{headerSize}'."); } - int skipAmount = 0; - if (headerSize > BmpInfoHeader.MaxHeaderSize) - { - skipAmount = headerSize - BmpInfoHeader.MaxHeaderSize; - headerSize = BmpInfoHeader.MaxHeaderSize; - } - // Read the rest of the header. this.stream.Read(buffer, BmpInfoHeader.HeaderSizeSize, headerSize - BmpInfoHeader.HeaderSizeSize); @@ -1169,15 +1162,13 @@ namespace SixLabors.ImageSharp.Formats.Bmp { this.bmpMetadata.BitsPerPixel = (BmpBitsPerPixel)bitsPerPixel; } - - // Skip the remaining header because we can't read those parts. - this.stream.Skip(skipAmount); } /// /// Reads the from the stream. /// - private void ReadFileHeader() + /// The color map size in bytes, if it could be determined by the file header. Otherwise -1. + private int ReadFileHeader() { #if NETCOREAPP2_1 Span buffer = stackalloc byte[BmpFileHeader.Size]; @@ -1186,12 +1177,36 @@ namespace SixLabors.ImageSharp.Formats.Bmp #endif this.stream.Read(buffer, 0, BmpFileHeader.Size); - this.fileHeader = BmpFileHeader.Parse(buffer); - - if (this.fileHeader.Type != BmpConstants.TypeMarkers.Bitmap) + short fileTypeMarker = BinaryPrimitives.ReadInt16LittleEndian(buffer); + switch (fileTypeMarker) { - BmpThrowHelper.ThrowNotSupportedException($"ImageSharp does not support this BMP file. File header bitmap type marker '{this.fileHeader.Type}'."); + case BmpConstants.TypeMarkers.Bitmap: + this.fileHeader = BmpFileHeader.Parse(buffer); + break; + case BmpConstants.TypeMarkers.BitmapArray: + // The Array file header is followed by the bitmap file header of the first image. + var arrayHeader = BmpArrayFileHeader.Parse(buffer); + this.stream.Read(buffer, 0, BmpFileHeader.Size); + this.fileHeader = BmpFileHeader.Parse(buffer); + if (this.fileHeader.Type != BmpConstants.TypeMarkers.Bitmap) + { + BmpThrowHelper.ThrowNotSupportedException($"Unsupported bitmap file inside a BitmapArray file. File header bitmap type marker '{this.fileHeader.Type}'."); + } + + if (arrayHeader.OffsetToNext != 0) + { + int colorMapSizeBytes = arrayHeader.OffsetToNext - arrayHeader.Size; + return colorMapSizeBytes; + } + + break; + + default: + BmpThrowHelper.ThrowNotSupportedException($"ImageSharp does not support this BMP file. File header bitmap type marker '{fileTypeMarker}'."); + break; } + + return -1; } /// @@ -1203,7 +1218,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp { this.stream = stream; - this.ReadFileHeader(); + int colorMapSizeBytes = this.ReadFileHeader(); this.ReadInfoHeader(); // see http://www.drdobbs.com/architecture-and-design/the-bmp-file-format-part-1/184409517 @@ -1218,7 +1233,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp this.infoHeader.Height = -this.infoHeader.Height; } - int colorMapSize = -1; int bytesPerColorMapEntry = 4; if (this.infoHeader.ClrUsed == 0) @@ -1227,35 +1241,38 @@ namespace SixLabors.ImageSharp.Formats.Bmp || this.infoHeader.BitsPerPixel == 4 || this.infoHeader.BitsPerPixel == 8) { - int colorMapSizeBytes = this.fileHeader.Offset - BmpFileHeader.Size - this.infoHeader.HeaderSize; + if (colorMapSizeBytes == -1) + { + colorMapSizeBytes = this.fileHeader.Offset - BmpFileHeader.Size - this.infoHeader.HeaderSize; + } + int colorCountForBitDepth = ImageMaths.GetColorCountForBitDepth(this.infoHeader.BitsPerPixel); bytesPerColorMapEntry = colorMapSizeBytes / colorCountForBitDepth; // Edge case for less-than-full-sized palette: bytesPerColorMapEntry should be at least 3. bytesPerColorMapEntry = Math.Max(bytesPerColorMapEntry, 3); - colorMapSize = colorMapSizeBytes; } } else { - colorMapSize = this.infoHeader.ClrUsed * bytesPerColorMapEntry; + colorMapSizeBytes = this.infoHeader.ClrUsed * bytesPerColorMapEntry; } palette = null; - if (colorMapSize > 0) + if (colorMapSizeBytes > 0) { // Usually the color palette is 1024 byte (256 colors * 4), but the documentation does not mention a size limit. // Make sure, that we will not read pass the bitmap offset (starting position of image data). - if ((this.stream.Position + colorMapSize) > this.fileHeader.Offset) + if ((this.stream.Position + colorMapSizeBytes) > this.fileHeader.Offset) { BmpThrowHelper.ThrowImageFormatException( - $"Reading the color map would read beyond the bitmap offset. Either the color map size of '{colorMapSize}' is invalid or the bitmap offset."); + $"Reading the color map would read beyond the bitmap offset. Either the color map size of '{colorMapSizeBytes}' is invalid or the bitmap offset."); } - palette = new byte[colorMapSize]; + palette = new byte[colorMapSizeBytes]; - this.stream.Read(palette, 0, colorMapSize); + this.stream.Read(palette, 0, colorMapSizeBytes); } this.infoHeader.VerifyDimensions(); diff --git a/src/ImageSharp/Formats/Bmp/BmpFileHeader.cs b/src/ImageSharp/Formats/Bmp/BmpFileHeader.cs index e39a2af0e4..661275fc90 100644 --- a/src/ImageSharp/Formats/Bmp/BmpFileHeader.cs +++ b/src/ImageSharp/Formats/Bmp/BmpFileHeader.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp internal readonly struct BmpFileHeader { /// - /// Defines of the data structure in the bitmap file. + /// Defines the size of the data structure in the bitmap file. /// public const int Size = 14; @@ -69,4 +69,4 @@ namespace SixLabors.ImageSharp.Formats.Bmp dest = this; } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs b/src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs index 4f862d9295..3d7510bc2a 100644 --- a/src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs +++ b/src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -22,7 +22,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp private bool IsSupportedFileFormat(ReadOnlySpan header) { - return header.Length >= this.HeaderSize && BinaryPrimitives.ReadInt16LittleEndian(header) == BmpConstants.TypeMarkers.Bitmap; + short fileTypeMarker = BinaryPrimitives.ReadInt16LittleEndian(header); + return header.Length >= this.HeaderSize && + (fileTypeMarker == BmpConstants.TypeMarkers.Bitmap || fileTypeMarker == BmpConstants.TypeMarkers.BitmapArray); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs b/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs index 6da5f73e3f..ca90020d85 100644 --- a/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs +++ b/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; using System.Buffers.Binary; @@ -51,10 +51,15 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// public const int SizeV4 = 108; + /// + /// Defines the size of the BITMAPINFOHEADER (BMP Version 5) data structure in the bitmap file. + /// + public const int SizeV5 = 124; + /// /// Defines the size of the biggest supported header data structure in the bitmap file. /// - public const int MaxHeaderSize = SizeV4; + public const int MaxHeaderSize = SizeV5; /// /// Defines the size of the field. @@ -272,7 +277,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// Parses the BITMAPCOREHEADER (BMP Version 2) consisting of the headerSize, width, height, planes, and bitsPerPixel fields (12 bytes). /// /// The data to parse. - /// Parsed header + /// The parsed header. /// public static BmpInfoHeader ParseCore(ReadOnlySpan data) { @@ -289,7 +294,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// are 4 bytes instead of 2, resulting in 16 bytes total. /// /// The data to parse. - /// Parsed header + /// The parsed header. /// public static BmpInfoHeader ParseOs22Short(ReadOnlySpan data) { @@ -406,7 +411,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// public static BmpInfoHeader ParseV4(ReadOnlySpan data) { - if (data.Length != SizeV4) + if (data.Length < SizeV4) { throw new ArgumentException(nameof(data), $"Must be {SizeV4} bytes. Was {data.Length} bytes."); } @@ -457,4 +462,4 @@ namespace SixLabors.ImageSharp.Formats.Bmp } } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index c4dfa724cc..a95703609b 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -343,7 +343,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp } [Theory] - [WithFile(Rgba32bf56AdobeV3, PixelTypes.Rgba32)] [WithFile(Rgb32h52AdobeV3, PixelTypes.Rgba32)] public void BmpDecoder_CanDecodeAdobeBmpv3(TestImageProvider provider) where TPixel : struct, IPixel @@ -355,6 +354,18 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp } } + [Theory] + [WithFile(Rgba32bf56AdobeV3, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecodeAdobeBmpv3_WithAlpha(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder())) + { + image.DebugSave(provider); + image.CompareToOriginal(provider, new MagickReferenceDecoder()); + } + } + [Theory] [WithFile(WinBmpv4, PixelTypes.Rgba32)] public void BmpDecoder_CanDecodeBmpv4(TestImageProvider provider) @@ -429,12 +440,35 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp [InlineData(Bit4, 4)] [InlineData(Bit1, 1)] [InlineData(Bit1Pal1, 1)] - public void Identify(string imagePath, int expectedPixelSize) + public void Identify_DetectsCorrectPixelType(string imagePath, int expectedPixelSize) { var testFile = TestFile.Create(imagePath); using (var stream = new MemoryStream(testFile.Bytes, false)) { - Assert.Equal(expectedPixelSize, Image.Identify(stream)?.PixelType?.BitsPerPixel); + IImageInfo imageInfo = Image.Identify(stream); + Assert.NotNull(imageInfo); + Assert.Equal(expectedPixelSize, imageInfo.PixelType?.BitsPerPixel); + } + } + + [Theory] + [InlineData(Bit32Rgb, 127, 64)] + [InlineData(Car, 600, 450)] + [InlineData(Bit16, 127, 64)] + [InlineData(Bit16Inverted, 127, 64)] + [InlineData(Bit8, 127, 64)] + [InlineData(Bit8Inverted, 127, 64)] + [InlineData(RLE8, 491, 272)] + [InlineData(RLE8Inverted, 491, 272)] + public void Identify_DetectsCorrectWidthAndHeight(string imagePath, int expectedWidth, int expectedHeight) + { + var testFile = TestFile.Create(imagePath); + using (var stream = new MemoryStream(testFile.Bytes, false)) + { + IImageInfo imageInfo = Image.Identify(stream); + Assert.NotNull(imageInfo); + Assert.Equal(expectedWidth, imageInfo.Width); + Assert.Equal(expectedHeight, imageInfo.Height); } } @@ -465,8 +499,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp { image.DebugSave(provider); - // TODO: Neither System.Drawing not MagickReferenceDecoder - // can correctly decode this file. + // TODO: Neither System.Drawing or MagickReferenceDecoder can correctly decode this file. // image.CompareToOriginal(provider); } } @@ -486,5 +519,26 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp // image.CompareToOriginal(provider, new MagickReferenceDecoder()); } } + + [Theory] + [WithFile(Os2BitmapArray9s, PixelTypes.Rgba32)] + [WithFile(Os2BitmapArrayDiamond, PixelTypes.Rgba32)] + [WithFile(Os2BitmapArraySkater, PixelTypes.Rgba32)] + [WithFile(Os2BitmapArraySpade, PixelTypes.Rgba32)] + [WithFile(Os2BitmapArraySunflower, PixelTypes.Rgba32)] + [WithFile(Os2BitmapArrayMarble, PixelTypes.Rgba32)] + [WithFile(Os2BitmapArrayWarpd, PixelTypes.Rgba32)] + [WithFile(Os2BitmapArrayPines, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecode_Os2BitmapArray(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder())) + { + image.DebugSave(provider); + + // TODO: Neither System.Drawing or MagickReferenceDecoder can correctly decode this file. + // image.CompareToOriginal(provider); + } + } } } diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs index 178e652ae7..7412f70a11 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System.IO; @@ -14,6 +14,8 @@ using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using Xunit; using Xunit.Abstractions; +// ReSharper disable InconsistentNaming + namespace SixLabors.ImageSharp.Tests.Formats.Bmp { using static TestImages.Bmp; @@ -262,4 +264,4 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp } } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpMetaDataTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpMetaDataTests.cs index ab72214f6c..da17dfb983 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpMetaDataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpMetaDataTests.cs @@ -1,11 +1,17 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System.IO; + using SixLabors.ImageSharp.Formats.Bmp; using Xunit; +// ReSharper disable InconsistentNaming + namespace SixLabors.ImageSharp.Tests.Formats.Bmp { + using static TestImages.Bmp; + public class BmpMetaDataTests { [Fact] @@ -18,5 +24,27 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp Assert.False(meta.BitsPerPixel.Equals(clone.BitsPerPixel)); } + + [Theory] + [InlineData(WinBmpv2, BmpInfoHeaderType.WinVersion2)] + [InlineData(WinBmpv3, BmpInfoHeaderType.WinVersion3)] + [InlineData(WinBmpv4, BmpInfoHeaderType.WinVersion4)] + [InlineData(WinBmpv5, BmpInfoHeaderType.WinVersion5)] + [InlineData(Os2v2Short, BmpInfoHeaderType.Os2Version2Short)] + [InlineData(Rgb32h52AdobeV3, BmpInfoHeaderType.AdobeVersion3)] + [InlineData(Rgba32bf56AdobeV3, BmpInfoHeaderType.AdobeVersion3WithAlpha)] + [InlineData(Os2v2, BmpInfoHeaderType.Os2Version2)] + public void Identify_DetectsCorrectBitmapInfoHeaderType(string imagePath, BmpInfoHeaderType expectedInfoHeaderType) + { + var testFile = TestFile.Create(imagePath); + using (var stream = new MemoryStream(testFile.Bytes, false)) + { + IImageInfo imageInfo = Image.Identify(stream); + Assert.NotNull(imageInfo); + BmpMetadata bitmapMetaData = imageInfo.Metadata.GetFormatMetadata(BmpFormat.Instance); + Assert.NotNull(bitmapMetaData); + Assert.Equal(expectedInfoHeaderType, bitmapMetaData.InfoHeaderType); + } + } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index d041f48544..1019a5b084 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -261,6 +261,14 @@ namespace SixLabors.ImageSharp.Tests public const string Bit8Palette4 = "Bmp/pal8-0.bmp"; public const string Os2v2Short = "Bmp/pal8os2v2-16.bmp"; public const string Os2v2 = "Bmp/pal8os2v2.bmp"; + public const string Os2BitmapArray9s = "Bmp/9S.BMP"; + public const string Os2BitmapArrayDiamond = "Bmp/DIAMOND.BMP"; + public const string Os2BitmapArrayMarble = "Bmp/GMARBLE.BMP"; + public const string Os2BitmapArraySkater = "Bmp/SKATER.BMP"; + public const string Os2BitmapArraySpade = "Bmp/SPADE.BMP"; + public const string Os2BitmapArraySunflower = "Bmp/SUNFLOW.BMP"; + public const string Os2BitmapArrayWarpd = "Bmp/WARPD.BMP"; + public const string Os2BitmapArrayPines = "Bmp/PINES.BMP"; public const string LessThanFullSizedPalette = "Bmp/pal8os2sp.bmp"; public const string Pal8Offset = "Bmp/pal8offs.bmp"; public const string OversizedPalette = "Bmp/pal8oversizepal.bmp"; diff --git a/tests/Images/Input/Bmp/9S.BMP b/tests/Images/Input/Bmp/9S.BMP new file mode 100644 index 0000000000..f6f937901c --- /dev/null +++ b/tests/Images/Input/Bmp/9S.BMP @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b54244f4ef0968568e0d8255707763f6c2027238ff66a22580ae12a019c33a9f +size 2684 diff --git a/tests/Images/Input/Bmp/DIAMOND.BMP b/tests/Images/Input/Bmp/DIAMOND.BMP new file mode 100644 index 0000000000..512b156358 --- /dev/null +++ b/tests/Images/Input/Bmp/DIAMOND.BMP @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1a7133850f670732c39cb343615f4443c934272002edfa5373a7286c1967e2cc +size 2684 diff --git a/tests/Images/Input/Bmp/GMARBLE.BMP b/tests/Images/Input/Bmp/GMARBLE.BMP new file mode 100644 index 0000000000..52a0fb7f0f --- /dev/null +++ b/tests/Images/Input/Bmp/GMARBLE.BMP @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:575d176563c98e71ec68ad9ee96e192393ba7a6296b0a27c55a3c1037fc18a96 +size 49585 diff --git a/tests/Images/Input/Bmp/PINES.BMP b/tests/Images/Input/Bmp/PINES.BMP new file mode 100644 index 0000000000..1bd55e4b19 --- /dev/null +++ b/tests/Images/Input/Bmp/PINES.BMP @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e6b6aa7b3f34cf370f7e34912fe5079b8e72cb5475722d658867d1a164957672 +size 61377 diff --git a/tests/Images/Input/Bmp/SKATER.BMP b/tests/Images/Input/Bmp/SKATER.BMP new file mode 100644 index 0000000000..0ba7113bee --- /dev/null +++ b/tests/Images/Input/Bmp/SKATER.BMP @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2a5d21e7eb2b48e277cd06c884dd98b66c2d036af57a8545316e0c68278bcf21 +size 7242 diff --git a/tests/Images/Input/Bmp/SPADE.BMP b/tests/Images/Input/Bmp/SPADE.BMP new file mode 100644 index 0000000000..e61e5e0c76 --- /dev/null +++ b/tests/Images/Input/Bmp/SPADE.BMP @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9f97211351e9fd297d81ab5cc359eab8fdf09c821c66458d722e3f3a80dc5e4a +size 2684 diff --git a/tests/Images/Input/Bmp/SUNFLOW.BMP b/tests/Images/Input/Bmp/SUNFLOW.BMP new file mode 100644 index 0000000000..08fb3070e2 --- /dev/null +++ b/tests/Images/Input/Bmp/SUNFLOW.BMP @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8456fc57a11d2d5f53eafc1aa49865db8df615bcbd4ccfd2a78e2ba024b9af2d +size 51753 diff --git a/tests/Images/Input/Bmp/WARPD.BMP b/tests/Images/Input/Bmp/WARPD.BMP new file mode 100644 index 0000000000..db9128bf37 --- /dev/null +++ b/tests/Images/Input/Bmp/WARPD.BMP @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ad3623de4da91c037866a2a504d5b9c62c48224a1b399b5cbb72fb6b5a5f5230 +size 2363136 From 7d4e461a19f3d5d00ea11fb03daaa9ecb7a39c61 Mon Sep 17 00:00:00 2001 From: Fredrik Eilertsen Date: Sat, 15 Jun 2019 01:27:29 +0200 Subject: [PATCH 220/223] Throw UnkownFormatException on Image.Load (#932) * Throw ImageFormatException on load * Unseal class and make constructor internal - This is so that no one can new it up / inherit it outside of the assembly * Add new exception for distinguish between different exception - This will be used on image.load operations with invalid image streams * ImageFormatException -> UnkownImageFormatException * Add Image.Load throws exception tests --- .../Common/Exceptions/ImageFormatException.cs | 8 +-- .../Exceptions/UnknownImageFormatException.cs | 36 +++++++++++++ src/ImageSharp/Image.FromStream.cs | 20 +++++-- ...ts.Load_FromStream_ThrowsRightException.cs | 52 +++++++++++++++++++ 4 files changed, 108 insertions(+), 8 deletions(-) create mode 100644 src/ImageSharp/Common/Exceptions/UnknownImageFormatException.cs create mode 100644 tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_ThrowsRightException.cs diff --git a/src/ImageSharp/Common/Exceptions/ImageFormatException.cs b/src/ImageSharp/Common/Exceptions/ImageFormatException.cs index 89877b1b66..8b9dbe1b80 100644 --- a/src/ImageSharp/Common/Exceptions/ImageFormatException.cs +++ b/src/ImageSharp/Common/Exceptions/ImageFormatException.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -9,14 +9,14 @@ namespace SixLabors.ImageSharp /// The exception that is thrown when the library tries to load /// an image, which has an invalid format. /// - public sealed class ImageFormatException : Exception + public class ImageFormatException : Exception { /// /// Initializes a new instance of the class with the name of the /// parameter that causes this exception. /// /// The error message that explains the reason for this exception. - public ImageFormatException(string errorMessage) + internal ImageFormatException(string errorMessage) : base(errorMessage) { } @@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp /// The error message that explains the reason for this exception. /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) /// if no inner exception is specified. - public ImageFormatException(string errorMessage, Exception innerException) + internal ImageFormatException(string errorMessage, Exception innerException) : base(errorMessage, innerException) { } diff --git a/src/ImageSharp/Common/Exceptions/UnknownImageFormatException.cs b/src/ImageSharp/Common/Exceptions/UnknownImageFormatException.cs new file mode 100644 index 0000000000..ca6e1fe6ed --- /dev/null +++ b/src/ImageSharp/Common/Exceptions/UnknownImageFormatException.cs @@ -0,0 +1,36 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +namespace SixLabors.ImageSharp +{ + /// + /// The exception that is thrown when the library tries to load + /// an image which has an unkown format. + /// + public sealed class UnknownImageFormatException : ImageFormatException + { + /// + /// Initializes a new instance of the class with the name of the + /// parameter that causes this exception. + /// + /// The error message that explains the reason for this exception. + public UnknownImageFormatException(string errorMessage) + : base(errorMessage) + { + } + + /// + /// Initializes a new instance of the class with a specified + /// error message and the exception that is the cause of this exception. + /// + /// The error message that explains the reason for this exception. + /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) + /// if no inner exception is specified. + public UnknownImageFormatException(string errorMessage, Exception innerException) + : base(errorMessage, innerException) + { + } + } +} diff --git a/src/ImageSharp/Image.FromStream.cs b/src/ImageSharp/Image.FromStream.cs index 74b99dfb3f..c4336c9aca 100644 --- a/src/ImageSharp/Image.FromStream.cs +++ b/src/ImageSharp/Image.FromStream.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -62,6 +62,7 @@ namespace SixLabors.ImageSharp /// The stream containing image information. /// the mime type of the decoded image. /// Thrown if the stream is not readable. + /// Image cannot be loaded. /// A new .> public static Image Load(Stream stream, out IImageFormat format) => Load(Configuration.Default, stream, out format); @@ -71,6 +72,7 @@ namespace SixLabors.ImageSharp /// /// The stream containing image information. /// Thrown if the stream is not readable. + /// Image cannot be loaded. /// A new .> public static Image Load(Stream stream) => Load(Configuration.Default, stream); @@ -81,6 +83,7 @@ namespace SixLabors.ImageSharp /// The stream containing image information. /// The decoder. /// Thrown if the stream is not readable. + /// Image cannot be loaded. /// A new .> public static Image Load(Stream stream, IImageDecoder decoder) => Load(Configuration.Default, stream, decoder); @@ -92,6 +95,7 @@ namespace SixLabors.ImageSharp /// The stream containing image information. /// The decoder. /// Thrown if the stream is not readable. + /// Image cannot be loaded. /// A new .> public static Image Load(Configuration config, Stream stream, IImageDecoder decoder) => WithSeekableStream(config, stream, s => decoder.Decode(config, s)); @@ -102,6 +106,7 @@ namespace SixLabors.ImageSharp /// The config for the decoder. /// The stream containing image information. /// Thrown if the stream is not readable. + /// Image cannot be loaded. /// A new .> public static Image Load(Configuration config, Stream stream) => Load(config, stream, out _); @@ -110,6 +115,7 @@ namespace SixLabors.ImageSharp /// /// The stream containing image information. /// Thrown if the stream is not readable. + /// Image cannot be loaded. /// The pixel format. /// A new .> public static Image Load(Stream stream) @@ -122,6 +128,7 @@ namespace SixLabors.ImageSharp /// The stream containing image information. /// the mime type of the decoded image. /// Thrown if the stream is not readable. + /// Image cannot be loaded. /// The pixel format. /// A new .> public static Image Load(Stream stream, out IImageFormat format) @@ -134,6 +141,7 @@ namespace SixLabors.ImageSharp /// The stream containing image information. /// The decoder. /// Thrown if the stream is not readable. + /// Image cannot be loaded. /// The pixel format. /// A new .> public static Image Load(Stream stream, IImageDecoder decoder) @@ -147,6 +155,7 @@ namespace SixLabors.ImageSharp /// The stream containing image information. /// The decoder. /// Thrown if the stream is not readable. + /// Image cannot be loaded. /// The pixel format. /// A new .> public static Image Load(Configuration config, Stream stream, IImageDecoder decoder) @@ -159,6 +168,7 @@ namespace SixLabors.ImageSharp /// The configuration options. /// The stream containing image information. /// Thrown if the stream is not readable. + /// Image cannot be loaded. /// The pixel format. /// A new .> public static Image Load(Configuration config, Stream stream) @@ -172,6 +182,7 @@ namespace SixLabors.ImageSharp /// The stream containing image information. /// the mime type of the decoded image. /// Thrown if the stream is not readable. + /// Image cannot be loaded. /// The pixel format. /// A new .> public static Image Load(Configuration config, Stream stream, out IImageFormat format) @@ -195,7 +206,7 @@ namespace SixLabors.ImageSharp sb.AppendLine($" - {val.Key.Name} : {val.Value.GetType().Name}"); } - throw new NotSupportedException(sb.ToString()); + throw new UnknownImageFormatException(sb.ToString()); } /// @@ -206,6 +217,7 @@ namespace SixLabors.ImageSharp /// The stream containing image information. /// the mime type of the decoded image. /// Thrown if the stream is not readable. + /// Image cannot be loaded. /// A new . public static Image Load(Configuration config, Stream stream, out IImageFormat format) { @@ -227,7 +239,7 @@ namespace SixLabors.ImageSharp sb.AppendLine($" - {val.Key.Name} : {val.Value.GetType().Name}"); } - throw new NotSupportedException(sb.ToString()); + throw new UnknownImageFormatException(sb.ToString()); } private static T WithSeekableStream(Configuration config, Stream stream, Func action) @@ -257,4 +269,4 @@ namespace SixLabors.ImageSharp } } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_ThrowsRightException.cs b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_ThrowsRightException.cs new file mode 100644 index 0000000000..df1f73c7a0 --- /dev/null +++ b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_ThrowsRightException.cs @@ -0,0 +1,52 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; + +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests +{ + public partial class ImageTests + { + public class Load_FromStream_Throws : IDisposable + { + private static readonly byte[] Data = new byte[] { 0x01 }; + + private MemoryStream Stream { get; } = new MemoryStream(Data); + + [Fact] + public void Image_Load_Throws_UknownImageFormatException() + { + Assert.Throws(() => + { + using (var img = Image.Load(Configuration.Default, this.Stream, out IImageFormat format)) + { + } + }); + } + + [Fact] + public void Image_Load_T_Throws_UknownImageFormatException() + { + Assert.Throws(() => + { + using (var img = Image.Load(Configuration.Default, this.Stream, out IImageFormat format)) + { + } + }); + } + + public void Dispose() + { + this.Stream?.Dispose(); + } + } + } +} From d5d69d0bdc5298bb18512396932ea4880fcf3b69 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 19 Jun 2019 11:07:36 +1000 Subject: [PATCH 221/223] Fix #937 (#938) --- src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs | 6 +++--- tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs | 7 ++++--- tests/ImageSharp.Tests/TestImages.cs | 1 + tests/Images/Input/Png/zlib-overflow2.png | 3 +++ 4 files changed, 11 insertions(+), 6 deletions(-) create mode 100644 tests/Images/Input/Png/zlib-overflow2.png diff --git a/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs b/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs index 71141a8939..405eeafeb9 100644 --- a/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs +++ b/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -140,7 +140,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib offset += bytesRead; - if (offset >= length) + if (offset >= length || offset >= count) { return bytesRead; } @@ -249,4 +249,4 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib this.compressedStream = new DeflateStream(this, CompressionMode.Decompress, true); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index 70d191d74e..5bb2db7848 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. // ReSharper disable InconsistentNaming @@ -42,7 +42,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png TestImages.Png.Rgb24BppTrans, TestImages.Png.GrayAlpha8Bit, TestImages.Png.Gray1BitTrans, - TestImages.Png.Bad.ZlibOverflow + TestImages.Png.Bad.ZlibOverflow, + TestImages.Png.Bad.ZlibOverflow2 }; public static readonly string[] TestImages48Bpp = @@ -294,4 +295,4 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png } } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 1019a5b084..3c732c32d3 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -87,6 +87,7 @@ namespace SixLabors.ImageSharp.Tests public const string ChunkLength2 = "Png/chunklength2.png"; public const string CorruptedChunk = "Png/big-corrupted-chunk.png"; public const string ZlibOverflow = "Png/zlib-overflow.png"; + public const string ZlibOverflow2 = "Png/zlib-overflow2.png"; } public static readonly string[] All = diff --git a/tests/Images/Input/Png/zlib-overflow2.png b/tests/Images/Input/Png/zlib-overflow2.png new file mode 100644 index 0000000000..bb95cfee8d --- /dev/null +++ b/tests/Images/Input/Png/zlib-overflow2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3994ef9f044fd589a412409b35cf23d346da43af0d82e25f2c11a36b464c7599 +size 16887 From 50848a6d276f39afe2f99f50bc2f8adcdcd74d36 Mon Sep 17 00:00:00 2001 From: Brian Popow <38701097+brianpopow@users.noreply.github.com> Date: Mon, 1 Jul 2019 07:12:48 +0200 Subject: [PATCH 222/223] Add support for decoding RLE24 Bitmaps (#939) * Add support for decoding RLE24 * Simplified determining colorMapSize, OS/2 always has 3 bytes for each palette entry * Enum value for RLE24 is remapped to a different value, to be clearly separate from valid windows values. --- src/ImageSharp/Formats/Bmp/BmpCompression.cs | 23 +- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 227 +++++++++++++++--- .../Formats/Bmp/BmpFileMarkerType.cs | 41 ++++ src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs | 6 +- .../Formats/Bmp/BmpDecoderTests.cs | 17 ++ tests/ImageSharp.Tests/TestImages.cs | 4 + tests/Images/Input/Bmp/ba-bm.bmp | 3 + tests/Images/Input/Bmp/rgb24rle24.bmp | 3 + tests/Images/Input/Bmp/rle24rlecut.bmp | 3 + tests/Images/Input/Bmp/rle24rletrns.bmp | 3 + 10 files changed, 296 insertions(+), 34 deletions(-) create mode 100644 src/ImageSharp/Formats/Bmp/BmpFileMarkerType.cs create mode 100644 tests/Images/Input/Bmp/ba-bm.bmp create mode 100644 tests/Images/Input/Bmp/rgb24rle24.bmp create mode 100644 tests/Images/Input/Bmp/rle24rlecut.bmp create mode 100644 tests/Images/Input/Bmp/rle24rletrns.bmp diff --git a/src/ImageSharp/Formats/Bmp/BmpCompression.cs b/src/ImageSharp/Formats/Bmp/BmpCompression.cs index be275019e6..27a0e121b6 100644 --- a/src/ImageSharp/Formats/Bmp/BmpCompression.cs +++ b/src/ImageSharp/Formats/Bmp/BmpCompression.cs @@ -1,10 +1,10 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Formats.Bmp { /// - /// Defines how the compression type of the image data + /// Defines the compression type of the image data /// in the bitmap file. /// internal enum BmpCompression : int @@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// /// Two bytes are one data record. If the first byte is not zero, the - /// next two half bytes will be repeated as much as the value of the first byte. + /// next byte will be repeated as much as the value of the first byte. /// If the first byte is zero, the record has different meanings, depending /// on the second byte. If the second byte is zero, it is the end of the row, /// if it is one, it is the end of the image. @@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// /// Two bytes are one data record. If the first byte is not zero, the - /// next byte will be repeated as much as the value of the first byte. + /// next two half bytes will be repeated as much as the value of the first byte. /// If the first byte is zero, the record has different meanings, depending /// on the second byte. If the second byte is zero, it is the end of the row, /// if it is one, it is the end of the image. @@ -60,6 +60,17 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// Specifies that the bitmap is not compressed and that the color table consists of four DWORD color /// masks that specify the red, green, blue, and alpha components of each pixel. /// - BI_ALPHABITFIELDS = 6 + BI_ALPHABITFIELDS = 6, + + /// + /// OS/2 specific compression type. + /// Similar to run length encoding of 4 and 8 bit. + /// The only difference is that run values encoded are three bytes in size (one byte per RGB color component), + /// rather than four or eight bits in size. + /// + /// Note: Because compression value of 4 is ambiguous for BI_RGB for windows and RLE24 for OS/2, the enum value is remapped + /// to a different value. + /// + RLE24 = 100, } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 1ceb35283a..906fc53fef 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -39,22 +39,22 @@ namespace SixLabors.ImageSharp.Formats.Bmp private const int DefaultRgb16BMask = 0x1F; /// - /// RLE8 flag value that indicates following byte has special meaning. + /// RLE flag value that indicates following byte has special meaning. /// private const int RleCommand = 0x00; /// - /// RLE8 flag value marking end of a scan line. + /// RLE flag value marking end of a scan line. /// private const int RleEndOfLine = 0x00; /// - /// RLE8 flag value marking end of bitmap data. + /// RLE flag value marking end of bitmap data. /// private const int RleEndOfBitmap = 0x01; /// - /// RLE8 flag value marking the start of [x,y] offset instruction. + /// RLE flag value marking the start of [x,y] offset instruction. /// private const int RleDelta = 0x02; @@ -78,6 +78,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// private BmpFileHeader fileHeader; + /// + /// Indicates which bitmap file marker was read. + /// + private BmpFileMarkerType fileMarkerType; + /// /// The info header containing detailed information about the bitmap. /// @@ -167,6 +172,12 @@ namespace SixLabors.ImageSharp.Formats.Bmp } break; + + case BmpCompression.RLE24: + this.ReadRle24(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); + + break; + case BmpCompression.RLE8: case BmpCompression.RLE4: this.ReadRle(this.infoHeader.Compression, pixels, palette, this.infoHeader.Width, this.infoHeader.Height, inverted); @@ -349,6 +360,75 @@ namespace SixLabors.ImageSharp.Formats.Bmp } } + /// + /// Looks up color values and builds the image from de-compressed RLE24. + /// + /// The pixel format. + /// The to assign the palette to. + /// The width of the bitmap. + /// The height of the bitmap. + /// Whether the bitmap is inverted. + private void ReadRle24(Buffer2D pixels, int width, int height, bool inverted) + where TPixel : struct, IPixel + { + TPixel color = default; + using (IMemoryOwner buffer = this.memoryAllocator.Allocate(width * height * 3, AllocationOptions.Clean)) + using (Buffer2D undefinedPixels = this.memoryAllocator.Allocate2D(width, height, AllocationOptions.Clean)) + using (IMemoryOwner rowsWithUndefinedPixels = this.memoryAllocator.Allocate(height, AllocationOptions.Clean)) + { + Span rowsWithUndefinedPixelsSpan = rowsWithUndefinedPixels.Memory.Span; + Span bufferSpan = buffer.GetSpan(); + this.UncompressRle24(width, bufferSpan, undefinedPixels.GetSpan(), rowsWithUndefinedPixelsSpan); + for (int y = 0; y < height; y++) + { + int newY = Invert(y, height, inverted); + Span pixelRow = pixels.GetRowSpan(newY); + bool rowHasUndefinedPixels = rowsWithUndefinedPixelsSpan[y]; + if (rowHasUndefinedPixels) + { + // Slow path with undefined pixels. + for (int x = 0; x < width; x++) + { + int idx = (y * width * 3) + (x * 3); + if (undefinedPixels[x, y]) + { + switch (this.options.RleSkippedPixelHandling) + { + case RleSkippedPixelHandling.FirstColorOfPalette: + color.FromBgr24(Unsafe.As(ref bufferSpan[idx])); + break; + case RleSkippedPixelHandling.Transparent: + color.FromVector4(Vector4.Zero); + break; + + // Default handling for skipped pixels is black (which is what System.Drawing is also doing). + default: + color.FromVector4(new Vector4(0.0f, 0.0f, 0.0f, 1.0f)); + break; + } + } + else + { + color.FromBgr24(Unsafe.As(ref bufferSpan[idx])); + } + + pixelRow[x] = color; + } + } + else + { + // Fast path without any undefined pixels. + for (int x = 0; x < width; x++) + { + int idx = (y * width * 3) + (x * 3); + color.FromBgr24(Unsafe.As(ref bufferSpan[idx])); + pixelRow[x] = color; + } + } + } + } + } + /// /// Produce uncompressed bitmap data from a RLE4 stream. /// @@ -545,7 +625,95 @@ namespace SixLabors.ImageSharp.Formats.Bmp } /// - /// Keeps track of skipped / undefined pixels, when EndOfBitmap command occurs. + /// Produce uncompressed bitmap data from a RLE24 stream. + /// + /// + ///
If first byte is 0, the second byte may have special meaning. + ///
Otherwise, the first byte is the length of the run and following three bytes are the color for the run. + ///
+ /// The width of the bitmap. + /// Buffer for uncompressed data. + /// Keeps track of skipped and therefore undefined pixels. + /// Keeps track of rows, which have undefined pixels. + private void UncompressRle24(int w, Span buffer, Span undefinedPixels, Span rowsWithUndefinedPixels) + { +#if NETCOREAPP2_1 + Span cmd = stackalloc byte[2]; +#else + byte[] cmd = new byte[2]; +#endif + int uncompressedPixels = 0; + + while (uncompressedPixels < buffer.Length) + { + if (this.stream.Read(cmd, 0, cmd.Length) != 2) + { + BmpThrowHelper.ThrowImageFormatException("Failed to read 2 bytes from stream while uncompressing RLE24 bitmap."); + } + + if (cmd[0] == RleCommand) + { + switch (cmd[1]) + { + case RleEndOfBitmap: + int skipEoB = (buffer.Length - (uncompressedPixels * 3)) / 3; + RleSkipEndOfBitmap(uncompressedPixels, w, skipEoB, undefinedPixels, rowsWithUndefinedPixels); + + return; + + case RleEndOfLine: + uncompressedPixels += RleSkipEndOfLine(uncompressedPixels, w, undefinedPixels, rowsWithUndefinedPixels); + + break; + + case RleDelta: + int dx = this.stream.ReadByte(); + int dy = this.stream.ReadByte(); + uncompressedPixels += RleSkipDelta(uncompressedPixels, w, dx, dy, undefinedPixels, rowsWithUndefinedPixels); + + break; + + default: + // If the second byte > 2, we are in 'absolute mode'. + // Take this number of bytes from the stream as uncompressed data. + int length = cmd[1]; + + byte[] run = new byte[length * 3]; + + this.stream.Read(run, 0, run.Length); + + run.AsSpan().CopyTo(buffer.Slice(start: uncompressedPixels * 3)); + + uncompressedPixels += length; + + // Absolute mode data is aligned to two-byte word-boundary. + int padding = run.Length & 1; + + this.stream.Skip(padding); + + break; + } + } + else + { + int max = uncompressedPixels + cmd[0]; + byte blueIdx = cmd[1]; + byte greenIdx = (byte)this.stream.ReadByte(); + byte redIdx = (byte)this.stream.ReadByte(); + + int bufferIdx = uncompressedPixels * 3; + for (; uncompressedPixels < max; uncompressedPixels++) + { + buffer[bufferIdx++] = blueIdx; + buffer[bufferIdx++] = greenIdx; + buffer[bufferIdx++] = redIdx; + } + } + } + } + + /// + /// Keeps track of skipped / undefined pixels, when the EndOfBitmap command occurs. /// /// The already processed pixel count. /// The width of the image. @@ -576,7 +744,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// /// Keeps track of undefined / skipped pixels, when the EndOfLine command occurs. /// - /// The already processed pixel count. + /// The already uncompressed pixel count. /// The width of image. /// The undefined pixels. /// The rows with undefined pixels. @@ -1167,8 +1335,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// /// Reads the from the stream. /// - /// The color map size in bytes, if it could be determined by the file header. Otherwise -1. - private int ReadFileHeader() + private void ReadFileHeader() { #if NETCOREAPP2_1 Span buffer = stackalloc byte[BmpFileHeader.Size]; @@ -1181,11 +1348,14 @@ namespace SixLabors.ImageSharp.Formats.Bmp switch (fileTypeMarker) { case BmpConstants.TypeMarkers.Bitmap: + this.fileMarkerType = BmpFileMarkerType.Bitmap; this.fileHeader = BmpFileHeader.Parse(buffer); break; case BmpConstants.TypeMarkers.BitmapArray: - // The Array file header is followed by the bitmap file header of the first image. - var arrayHeader = BmpArrayFileHeader.Parse(buffer); + this.fileMarkerType = BmpFileMarkerType.BitmapArray; + + // Because we only decode the first bitmap in the array, the array header will be ignored. + // The bitmap file header of the first image follows the array header. this.stream.Read(buffer, 0, BmpFileHeader.Size); this.fileHeader = BmpFileHeader.Parse(buffer); if (this.fileHeader.Type != BmpConstants.TypeMarkers.Bitmap) @@ -1193,20 +1363,12 @@ namespace SixLabors.ImageSharp.Formats.Bmp BmpThrowHelper.ThrowNotSupportedException($"Unsupported bitmap file inside a BitmapArray file. File header bitmap type marker '{this.fileHeader.Type}'."); } - if (arrayHeader.OffsetToNext != 0) - { - int colorMapSizeBytes = arrayHeader.OffsetToNext - arrayHeader.Size; - return colorMapSizeBytes; - } - break; default: BmpThrowHelper.ThrowNotSupportedException($"ImageSharp does not support this BMP file. File header bitmap type marker '{fileTypeMarker}'."); break; } - - return -1; } /// @@ -1218,7 +1380,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp { this.stream = stream; - int colorMapSizeBytes = this.ReadFileHeader(); + this.ReadFileHeader(); this.ReadInfoHeader(); // see http://www.drdobbs.com/architecture-and-design/the-bmp-file-format-part-1/184409517 @@ -1234,23 +1396,34 @@ namespace SixLabors.ImageSharp.Formats.Bmp } int bytesPerColorMapEntry = 4; - + int colorMapSizeBytes = -1; if (this.infoHeader.ClrUsed == 0) { if (this.infoHeader.BitsPerPixel == 1 || this.infoHeader.BitsPerPixel == 4 || this.infoHeader.BitsPerPixel == 8) { - if (colorMapSizeBytes == -1) + switch (this.fileMarkerType) { - colorMapSizeBytes = this.fileHeader.Offset - BmpFileHeader.Size - this.infoHeader.HeaderSize; - } + case BmpFileMarkerType.Bitmap: + colorMapSizeBytes = this.fileHeader.Offset - BmpFileHeader.Size - this.infoHeader.HeaderSize; + int colorCountForBitDepth = ImageMaths.GetColorCountForBitDepth(this.infoHeader.BitsPerPixel); + bytesPerColorMapEntry = colorMapSizeBytes / colorCountForBitDepth; - int colorCountForBitDepth = ImageMaths.GetColorCountForBitDepth(this.infoHeader.BitsPerPixel); - bytesPerColorMapEntry = colorMapSizeBytes / colorCountForBitDepth; + // Edge case for less-than-full-sized palette: bytesPerColorMapEntry should be at least 3. + bytesPerColorMapEntry = Math.Max(bytesPerColorMapEntry, 3); - // Edge case for less-than-full-sized palette: bytesPerColorMapEntry should be at least 3. - bytesPerColorMapEntry = Math.Max(bytesPerColorMapEntry, 3); + break; + case BmpFileMarkerType.BitmapArray: + case BmpFileMarkerType.ColorIcon: + case BmpFileMarkerType.ColorPointer: + case BmpFileMarkerType.Icon: + case BmpFileMarkerType.Pointer: + // OS/2 bitmaps always have 3 colors per color palette entry. + bytesPerColorMapEntry = 3; + colorMapSizeBytes = ImageMaths.GetColorCountForBitDepth(this.infoHeader.BitsPerPixel) * bytesPerColorMapEntry; + break; + } } } else diff --git a/src/ImageSharp/Formats/Bmp/BmpFileMarkerType.cs b/src/ImageSharp/Formats/Bmp/BmpFileMarkerType.cs new file mode 100644 index 0000000000..4abcaa3a08 --- /dev/null +++ b/src/ImageSharp/Formats/Bmp/BmpFileMarkerType.cs @@ -0,0 +1,41 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats.Bmp +{ + /// + /// Indicates which bitmap file marker was read. + /// + public enum BmpFileMarkerType + { + /// + /// Single-image BMP file that may have been created under Windows or OS/2. + /// + Bitmap, + + /// + /// OS/2 Bitmap Array. + /// + BitmapArray, + + /// + /// OS/2 Color Icon. + /// + ColorIcon, + + /// + /// OS/2 Color Pointer. + /// + ColorPointer, + + /// + /// OS/2 Icon. + /// + Icon, + + /// + /// OS/2 Pointer. + /// + Pointer + } +} diff --git a/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs b/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs index ca90020d85..4d7f781001 100644 --- a/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs +++ b/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs @@ -388,8 +388,12 @@ namespace SixLabors.ImageSharp.Formats.Bmp case 2: infoHeader.Compression = BmpCompression.RLE4; break; + case 4: + infoHeader.Compression = BmpCompression.RLE24; + break; default: - BmpThrowHelper.ThrowImageFormatException($"Compression type is not supported. ImageSharp only supports uncompressed, RLE4 and RLE8."); + // Compression type 3 (1DHuffman) is not supported. + BmpThrowHelper.ThrowImageFormatException("Compression type is not supported. ImageSharp only supports uncompressed, RLE4, RLE8 and RLE24."); break; } diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index a95703609b..dadce92d13 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -228,6 +228,22 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp } } + [Theory] + [WithFile(RLE24, PixelTypes.Rgba32)] + [WithFile(RLE24Cut, PixelTypes.Rgba32)] + [WithFile(RLE24Delta, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecode_RunLengthEncoded_24Bit(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder() { RleSkippedPixelHandling = RleSkippedPixelHandling.Black })) + { + image.DebugSave(provider); + + // TODO: Neither System.Drawing nor MagickReferenceDecoder decode this file. + // image.CompareToOriginal(provider); + } + } + [Theory] [WithFile(RgbaAlphaBitfields, PixelTypes.Rgba32)] public void BmpDecoder_CanDecodeAlphaBitfields(TestImageProvider provider) @@ -521,6 +537,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp } [Theory] + [WithFile(Os2BitmapArray, PixelTypes.Rgba32)] [WithFile(Os2BitmapArray9s, PixelTypes.Rgba32)] [WithFile(Os2BitmapArrayDiamond, PixelTypes.Rgba32)] [WithFile(Os2BitmapArraySkater, PixelTypes.Rgba32)] diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 3c732c32d3..754ce20ca9 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -231,6 +231,9 @@ namespace SixLabors.ImageSharp.Tests public const string NegHeight = "Bmp/neg_height.bmp"; public const string CoreHeader = "Bmp/BitmapCoreHeaderQR.bmp"; public const string V5Header = "Bmp/BITMAPV5HEADER.bmp"; + public const string RLE24 = "Bmp/rgb24rle24.bmp"; + public const string RLE24Cut = "Bmp/rle24rlecut.bmp"; + public const string RLE24Delta = "Bmp/rle24rlecut.bmp"; public const string RLE8 = "Bmp/RunLengthEncoded.bmp"; public const string RLE8Cut = "Bmp/pal8rlecut.bmp"; public const string RLE8Delta = "Bmp/pal8rletrns.bmp"; @@ -262,6 +265,7 @@ namespace SixLabors.ImageSharp.Tests public const string Bit8Palette4 = "Bmp/pal8-0.bmp"; public const string Os2v2Short = "Bmp/pal8os2v2-16.bmp"; public const string Os2v2 = "Bmp/pal8os2v2.bmp"; + public const string Os2BitmapArray = "Bmp/ba-bm.bmp"; public const string Os2BitmapArray9s = "Bmp/9S.BMP"; public const string Os2BitmapArrayDiamond = "Bmp/DIAMOND.BMP"; public const string Os2BitmapArrayMarble = "Bmp/GMARBLE.BMP"; diff --git a/tests/Images/Input/Bmp/ba-bm.bmp b/tests/Images/Input/Bmp/ba-bm.bmp new file mode 100644 index 0000000000..a787229ac2 --- /dev/null +++ b/tests/Images/Input/Bmp/ba-bm.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5ec70510334952d3fbeae51a9a49d4e50e5afc292a1f9232970a7cf22b1a18fc +size 9000 diff --git a/tests/Images/Input/Bmp/rgb24rle24.bmp b/tests/Images/Input/Bmp/rgb24rle24.bmp new file mode 100644 index 0000000000..0e0731dd54 --- /dev/null +++ b/tests/Images/Input/Bmp/rgb24rle24.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:351d358824671a79dc63147a78fc555d46cbee357661674e80c898e133e0b5c5 +size 21432 diff --git a/tests/Images/Input/Bmp/rle24rlecut.bmp b/tests/Images/Input/Bmp/rle24rlecut.bmp new file mode 100644 index 0000000000..137d38647a --- /dev/null +++ b/tests/Images/Input/Bmp/rle24rlecut.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:15b84ee3e41934653939197267758e6719da93d017200a7b9e61820b368af04c +size 16748 diff --git a/tests/Images/Input/Bmp/rle24rletrns.bmp b/tests/Images/Input/Bmp/rle24rletrns.bmp new file mode 100644 index 0000000000..bc5dc14a96 --- /dev/null +++ b/tests/Images/Input/Bmp/rle24rletrns.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:37caf0742ebc94e4ff73b822052091db543559fa96352b83a3e5f5545999c5f7 +size 20036 From 96c3bbff79937125969ec4e69079923f64358613 Mon Sep 17 00:00:00 2001 From: Anton Firsov Date: Mon, 1 Jul 2019 08:31:52 +0200 Subject: [PATCH 223/223] Introduce non-generic ImageFrameCollection (#941) * temporarily disable target frameworks * drop DelegateProcessor * drop IImageProcessingContext * drop NamedColors * drop ColorBuilder * drop the *Base postfix for clean class hierarchies * adding basic skeletons * non-generic ImageFrameCollection API definition * non-generic ImageFrameCollection tests * cleanup + docs + more tests * implement ImageFrameCollection methods * tests for generic PixelOperations.To() * experimental implementation * fix .ttinclude * generate generic From(...) * fix RgbaVector <--> BT709 Gray pixel conversion * Gray8 and Gray16 using ConvertFromRgbaScaledVector4() by default * fixed all conversion tests * ConstructGif_FromDifferentPixelTypes * fix xmldoc and other StyelCop findings * re-enable all target frameworks * fix NonGenericAddFrame() and NonGenericInsertFrame() * fix remaining bugs --- .gitattributes | 2 +- src/ImageSharp/Common/Helpers/ImageMaths.cs | 13 +- .../Decoder/JpegImagePostProcessor.cs | 4 +- src/ImageSharp/Image.cs | 12 +- src/ImageSharp/ImageFrame.LoadPixelData.cs | 10 +- src/ImageSharp/ImageFrame.cs | 91 +++++ src/ImageSharp/ImageFrameCollection.cs | 252 ++++-------- .../ImageFrameCollection{TPixel}.cs | 358 ++++++++++++++++++ src/ImageSharp/ImageFrame{TPixel}.cs | 84 ++-- src/ImageSharp/ImageSharp.csproj | 10 + src/ImageSharp/Image{TPixel}.cs | 27 +- .../Argb32.PixelOperations.Generated.cs | 12 +- .../Argb32.PixelOperations.Generated.tt | 2 +- .../Bgr24.PixelOperations.Generated.cs | 12 +- .../Bgr24.PixelOperations.Generated.tt | 2 +- .../Bgra32.PixelOperations.Generated.cs | 12 +- .../Bgra32.PixelOperations.Generated.tt | 2 +- .../Gray16.PixelOperations.Generated.cs | 12 +- .../Gray16.PixelOperations.Generated.tt | 2 +- .../Gray8.PixelOperations.Generated.cs | 12 +- .../Gray8.PixelOperations.Generated.tt | 2 +- .../Rgb24.PixelOperations.Generated.cs | 12 +- .../Rgb24.PixelOperations.Generated.tt | 2 +- .../Rgb48.PixelOperations.Generated.cs | 12 +- .../Rgb48.PixelOperations.Generated.tt | 2 +- .../Rgba32.PixelOperations.Generated.cs | 12 +- .../Rgba32.PixelOperations.Generated.tt | 2 +- .../Rgba64.PixelOperations.Generated.cs | 12 +- .../Rgba64.PixelOperations.Generated.tt | 2 +- .../Generated/_Common.ttinclude | 17 +- .../PixelImplementations/Gray16.cs | 16 +- .../PixelImplementations/Gray8.cs | 14 +- .../RgbaVector.PixelOperations.cs | 35 +- .../PixelFormats/PixelOperations{TPixel}.cs | 91 +++-- .../AffineTransformProcessor{TPixel}.cs | 2 +- .../Transforms/CropProcessor{TPixel}.cs | 7 +- .../ProjectiveTransformProcessor{TPixel}.cs | 4 +- .../Resize/ResizeProcessor{TPixel}.cs | 7 +- .../ImageFrameCollectionTests.Generic.cs | 346 +++++++++++++++++ .../ImageFrameCollectionTests.NonGeneric.cs | 325 ++++++++++++++++ .../Image/ImageFrameCollectionTests.cs | 29 ++ .../Image/ImageFramesCollectionTests.cs | 331 ---------------- .../ImageSharp.Tests/ImageSharp.Tests.csproj | 7 + ...ConverterTests.ReferenceImplementations.cs | 115 ++++++ .../PixelFormats/PixelConverterTests.cs | 38 +- ...OperationsTests.Bgra5551OperationsTests.cs | 20 + ...elOperationsTests.Gray16OperationsTests.cs | 87 +---- ...xelOperationsTests.Gray8OperationsTests.cs | 87 +---- ...xelOperationsTests.Rgb24OperationsTests.cs | 2 +- .../PixelOperations/PixelOperationsTests.cs | 131 ++++++- 50 files changed, 1824 insertions(+), 876 deletions(-) create mode 100644 src/ImageSharp/ImageFrame.cs create mode 100644 src/ImageSharp/ImageFrameCollection{TPixel}.cs create mode 100644 tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs create mode 100644 tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.NonGeneric.cs create mode 100644 tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.cs delete mode 100644 tests/ImageSharp.Tests/Image/ImageFramesCollectionTests.cs create mode 100644 tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.ReferenceImplementations.cs create mode 100644 tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Bgra5551OperationsTests.cs diff --git a/.gitattributes b/.gitattributes index 704a6aa436..38abe0d4a4 100644 --- a/.gitattributes +++ b/.gitattributes @@ -45,7 +45,7 @@ *.svg text eol=lf *.targets text eol=lf *.tt text eol=lf -*.ttinclude text eol=lf +*.ttinclude text eol=crlf *.txt text eol=lf *.vb text eol=lf *.yml text eol=lf diff --git a/src/ImageSharp/Common/Helpers/ImageMaths.cs b/src/ImageSharp/Common/Helpers/ImageMaths.cs index 64bcb11c9f..7460c9cac1 100644 --- a/src/ImageSharp/Common/Helpers/ImageMaths.cs +++ b/src/ImageSharp/Common/Helpers/ImageMaths.cs @@ -36,6 +36,17 @@ namespace SixLabors.ImageSharp public static ushort Get16BitBT709Luminance(ushort r, ushort g, ushort b) => (ushort)((r * .2126F) + (g * .7152F) + (b * .0722F)); + /// + /// Gets the luminance from the rgb components using the formula as specified by ITU-R Recommendation BT.709. + /// + /// The red component. + /// The green component. + /// The blue component. + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public static ushort Get16BitBT709Luminance(float r, float g, float b) => + (ushort)((r * .2126F) + (g * .7152F) + (b * .0722F)); + /// /// Scales a value from a 16 bit to it's 8 bit equivalent. /// @@ -379,4 +390,4 @@ namespace SixLabors.ImageSharp return GetBoundingRectangle(topLeft, bottomRight); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs index 2c81908843..f3f2952b1d 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs @@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// (4) Packing pixels from the buffer.
/// These operations are executed in steps. /// image rows are converted in one step, - /// which means that size of the allocated memory is limited (does not depend on ). + /// which means that size of the allocated memory is limited (does not depend on ). ///
internal class JpegImagePostProcessor : IDisposable { @@ -177,4 +177,4 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Image.cs b/src/ImageSharp/Image.cs index 3f3d6a62f7..86fef715d1 100644 --- a/src/ImageSharp/Image.cs +++ b/src/ImageSharp/Image.cs @@ -53,6 +53,11 @@ namespace SixLabors.ImageSharp ///
protected Configuration Configuration { get; } + /// + /// Gets the implementing the public property. + /// + protected abstract ImageFrameCollection NonGenericFrameCollection { get; } + /// public PixelTypeInfo PixelType { get; } @@ -65,6 +70,11 @@ namespace SixLabors.ImageSharp /// public ImageMetadata Metadata { get; } + /// + /// Gets the frames of the image as (non-generic) . + /// + public ImageFrameCollection Frames => this.NonGenericFrameCollection; + /// /// Gets the pixel buffer. /// @@ -137,4 +147,4 @@ namespace SixLabors.ImageSharp } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/ImageFrame.LoadPixelData.cs b/src/ImageSharp/ImageFrame.LoadPixelData.cs index 33dbe31df7..9e90aeaf59 100644 --- a/src/ImageSharp/ImageFrame.LoadPixelData.cs +++ b/src/ImageSharp/ImageFrame.LoadPixelData.cs @@ -9,9 +9,9 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp { /// - /// Adds static methods allowing the creation of new image from raw pixel data. + /// Contains methods for loading raw pixel data. /// - internal static class ImageFrame + public partial class ImageFrame { /// /// Create a new instance of the class from the given byte array in format. @@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp /// The height of the final image. /// The pixel format. /// A new . - public static ImageFrame LoadPixelData(Configuration configuration, ReadOnlySpan data, int width, int height) + internal static ImageFrame LoadPixelData(Configuration configuration, ReadOnlySpan data, int width, int height) where TPixel : struct, IPixel => LoadPixelData(configuration, MemoryMarshal.Cast(data), width, height); @@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp /// The height of the final image. /// The pixel format. /// A new . - public static ImageFrame LoadPixelData(Configuration configuration, ReadOnlySpan data, int width, int height) + internal static ImageFrame LoadPixelData(Configuration configuration, ReadOnlySpan data, int width, int height) where TPixel : struct, IPixel { int count = width * height; @@ -48,4 +48,4 @@ namespace SixLabors.ImageSharp return image; } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/ImageFrame.cs b/src/ImageSharp/ImageFrame.cs new file mode 100644 index 0000000000..f3fe1ed8d4 --- /dev/null +++ b/src/ImageSharp/ImageFrame.cs @@ -0,0 +1,91 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +using SixLabors.ImageSharp.Metadata; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Memory; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp +{ + /// + /// Represents a pixel-agnostic image frame containing all pixel data and . + /// In case of animated formats like gif, it contains the single frame in a animation. + /// In all other cases it is the only frame of the image. + /// + public abstract partial class ImageFrame : IDisposable + { + /// + /// Initializes a new instance of the class. + /// + /// The . + /// The width. + /// The height. + /// The . + protected ImageFrame(Configuration configuration, int width, int height, ImageFrameMetadata metadata) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.NotNull(metadata, nameof(metadata)); + + this.Configuration = configuration; + this.MemoryAllocator = configuration.MemoryAllocator; + this.Width = width; + this.Height = height; + this.Metadata = metadata; + } + + /// + /// Gets the to use for buffer allocations. + /// + public MemoryAllocator MemoryAllocator { get; } + + /// + /// Gets the instance associated with this . + /// + internal Configuration Configuration { get; } + + /// + /// Gets the width. + /// + public int Width { get; private set; } + + /// + /// Gets the height. + /// + public int Height { get; private set; } + + /// + /// Gets the metadata of the frame. + /// + public ImageFrameMetadata Metadata { get; } + + /// + /// Gets the size of the frame. + /// + /// The + public Size Size() => new Size(this.Width, this.Height); + + /// + /// Gets the bounds of the frame. + /// + /// The + public Rectangle Bounds() => new Rectangle(0, 0, this.Width, this.Height); + + /// + public abstract void Dispose(); + + internal abstract void CopyPixelsTo(Span destination) + where TDestinationPixel : struct, IPixel; + + /// + /// Updates the size of the image frame. + /// + internal void UpdateSize(Size size) + { + this.Width = size.Width; + this.Height = size.Height; + } + } +} diff --git a/src/ImageSharp/ImageFrameCollection.cs b/src/ImageSharp/ImageFrameCollection.cs index 7e516434cb..c5bd02c79d 100644 --- a/src/ImageSharp/ImageFrameCollection.cs +++ b/src/ImageSharp/ImageFrameCollection.cs @@ -4,149 +4,69 @@ using System; using System.Collections; using System.Collections.Generic; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp { /// - /// Encapsulates a collection of instances that make up an . + /// Encapsulates a pixel-agnostic collection of instances + /// that make up an . /// - /// The type of the pixel. - public sealed class ImageFrameCollection : IEnumerable> - where TPixel : struct, IPixel + public abstract class ImageFrameCollection : IEnumerable { - private readonly IList> frames = new List>(); - private readonly Image parent; - - internal ImageFrameCollection(Image parent, int width, int height, TPixel backgroundColor) - { - this.parent = parent ?? throw new ArgumentNullException(nameof(parent)); - - // Frames are already cloned within the caller - this.frames.Add(new ImageFrame(parent.GetConfiguration(), width, height, backgroundColor)); - } - - internal ImageFrameCollection(Image parent, int width, int height, MemorySource memorySource) - { - this.parent = parent ?? throw new ArgumentNullException(nameof(parent)); - - // Frames are already cloned within the caller - this.frames.Add(new ImageFrame(parent.GetConfiguration(), width, height, memorySource)); - } - - internal ImageFrameCollection(Image parent, IEnumerable> frames) - { - Guard.NotNull(parent, nameof(parent)); - Guard.NotNull(frames, nameof(frames)); - - this.parent = parent; - - // Frames are already cloned by the caller - foreach (ImageFrame f in frames) - { - this.ValidateFrame(f); - this.frames.Add(f); - } - - // Ensure at least 1 frame was added to the frames collection - if (this.frames.Count == 0) - { - throw new ArgumentException("Must not be empty.", nameof(frames)); - } - } - /// /// Gets the number of frames. /// - public int Count => this.frames.Count; + public abstract int Count { get; } /// /// Gets the root frame. /// - public ImageFrame RootFrame => this.frames.Count > 0 ? this.frames[0] : null; + public ImageFrame RootFrame => this.NonGenericRootFrame; + + /// + /// Gets the root frame. (Implements .) + /// + protected abstract ImageFrame NonGenericRootFrame { get; } /// - /// Gets the at the specified index. + /// Gets the at the specified index. /// /// - /// The . + /// The . /// /// The index. - /// The at the specified index. - public ImageFrame this[int index] => this.frames[index]; + /// The at the specified index. + public ImageFrame this[int index] => this.NonGenericGetFrame(index); /// - /// Determines the index of a specific in the . + /// Determines the index of a specific in the . /// - /// The to locate in the . + /// The to locate in the . /// The index of item if found in the list; otherwise, -1. - public int IndexOf(ImageFrame frame) => this.frames.IndexOf(frame); + public abstract int IndexOf(ImageFrame frame); /// - /// Clones and inserts the into the at the specified . + /// Clones and inserts the into the at the specified . /// /// The zero-based index to insert the frame at. - /// The to clone and insert into the . + /// The to clone and insert into the . /// Frame must have the same dimensions as the image. - /// The cloned . - public ImageFrame InsertFrame(int index, ImageFrame source) - { - this.ValidateFrame(source); - ImageFrame clonedFrame = source.Clone(this.parent.GetConfiguration()); - this.frames.Insert(index, clonedFrame); - return clonedFrame; - } + /// The cloned . + public ImageFrame InsertFrame(int index, ImageFrame source) => this.NonGenericInsertFrame(index, source); /// /// Clones the frame and appends the clone to the end of the collection. /// /// The raw pixel data to generate the from. /// The cloned . - public ImageFrame AddFrame(ImageFrame source) - { - this.ValidateFrame(source); - ImageFrame clonedFrame = source.Clone(this.parent.GetConfiguration()); - this.frames.Add(clonedFrame); - return clonedFrame; - } - - /// - /// Creates a new frame from the pixel data with the same dimensions as the other frames and inserts the - /// new frame at the end of the collection. - /// - /// The raw pixel data to generate the from. - /// The new . - public ImageFrame AddFrame(TPixel[] source) - { - Guard.NotNull(source, nameof(source)); - - var frame = ImageFrame.LoadPixelData( - this.parent.GetConfiguration(), - new ReadOnlySpan(source), - this.RootFrame.Width, - this.RootFrame.Height); - this.frames.Add(frame); - return frame; - } + public ImageFrame AddFrame(ImageFrame source) => this.NonGenericAddFrame(source); /// /// Removes the frame at the specified index and frees all freeable resources associated with it. /// /// The zero-based index of the frame to remove. /// Cannot remove last frame. - public void RemoveFrame(int index) - { - if (index == 0 && this.Count == 1) - { - throw new InvalidOperationException("Cannot remove last frame."); - } - - ImageFrame frame = this.frames[index]; - this.frames.RemoveAt(index); - frame.Dispose(); - } + public abstract void RemoveFrame(int index); /// /// Determines whether the contains the . @@ -155,24 +75,14 @@ namespace SixLabors.ImageSharp /// /// true if the contains the specified frame; otherwise, false. /// - public bool Contains(ImageFrame frame) => this.frames.Contains(frame); + public abstract bool Contains(ImageFrame frame); /// /// Moves an from to . /// /// The zero-based index of the frame to move. /// The index to move the frame to. - public void MoveFrame(int sourceIndex, int destinationIndex) - { - if (sourceIndex == destinationIndex) - { - return; - } - - ImageFrame frameAtIndex = this.frames[sourceIndex]; - this.frames.RemoveAt(sourceIndex); - this.frames.Insert(destinationIndex, frameAtIndex); - } + public abstract void MoveFrame(int sourceIndex, int destinationIndex); /// /// Removes the frame at the specified index and creates a new image with only the removed frame @@ -181,19 +91,7 @@ namespace SixLabors.ImageSharp /// The zero-based index of the frame to export. /// Cannot remove last frame. /// The new with the specified frame. - public Image ExportFrame(int index) - { - ImageFrame frame = this[index]; - - if (this.Count == 1 && this.frames.Contains(frame)) - { - throw new InvalidOperationException("Cannot remove last frame."); - } - - this.frames.Remove(frame); - - return new Image(this.parent.GetConfiguration(), this.parent.Metadata.DeepClone(), new[] { frame }); - } + public Image ExportFrame(int index) => this.NonGenericExportFrame(index); /// /// Creates an with only the frame at the specified index @@ -201,12 +99,7 @@ namespace SixLabors.ImageSharp /// /// The zero-based index of the frame to clone. /// The new with the specified frame. - public Image CloneFrame(int index) - { - ImageFrame frame = this[index]; - ImageFrame clonedFrame = frame.Clone(); - return new Image(this.parent.GetConfiguration(), this.parent.Metadata.DeepClone(), new[] { clonedFrame }); - } + public Image CloneFrame(int index) => this.NonGenericCloneFrame(index); /// /// Creates a new and appends it to the end of the collection. @@ -214,7 +107,7 @@ namespace SixLabors.ImageSharp /// /// The new . /// - public ImageFrame CreateFrame() => this.CreateFrame(default); + public ImageFrame CreateFrame() => this.NonGenericCreateFrame(); /// /// Creates a new and appends it to the end of the collection. @@ -223,44 +116,67 @@ namespace SixLabors.ImageSharp /// /// The new . /// - public ImageFrame CreateFrame(TPixel backgroundColor) - { - var frame = new ImageFrame( - this.parent.GetConfiguration(), - this.RootFrame.Width, - this.RootFrame.Height, - backgroundColor); - this.frames.Add(frame); - return frame; - } + public ImageFrame CreateFrame(Color backgroundColor) => this.NonGenericCreateFrame(backgroundColor); - /// - IEnumerator> IEnumerable>.GetEnumerator() => this.frames.GetEnumerator(); + /// + public IEnumerator GetEnumerator() => this.NonGenericGetEnumerator(); /// - IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)this.frames).GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => this.NonGenericGetEnumerator(); - private void ValidateFrame(ImageFrame frame) - { - Guard.NotNull(frame, nameof(frame)); + /// + /// Implements . + /// + /// The enumerator. + protected abstract IEnumerator NonGenericGetEnumerator(); + + /// + /// Implements the getter of the indexer. + /// + /// The index. + /// The frame. + protected abstract ImageFrame NonGenericGetFrame(int index); + + /// + /// Implements . + /// + /// The index. + /// The frame. + /// The new frame. + protected abstract ImageFrame NonGenericInsertFrame(int index, ImageFrame source); + + /// + /// Implements . + /// + /// The frame. + /// The new frame. + protected abstract ImageFrame NonGenericAddFrame(ImageFrame source); + + /// + /// Implements . + /// + /// The index. + /// The new image. + protected abstract Image NonGenericExportFrame(int index); - if (this.Count != 0) - { - if (this.RootFrame.Width != frame.Width || this.RootFrame.Height != frame.Height) - { - throw new ArgumentException("Frame must have the same dimensions as the image.", nameof(frame)); - } - } - } + /// + /// Implements . + /// + /// The index. + /// The new image. + protected abstract Image NonGenericCloneFrame(int index); - internal void Dispose() - { - foreach (ImageFrame f in this.frames) - { - f.Dispose(); - } + /// + /// Implements . + /// + /// The new frame. + protected abstract ImageFrame NonGenericCreateFrame(); - this.frames.Clear(); - } + /// + /// Implements . + /// + /// The background color. + /// The new frame. + protected abstract ImageFrame NonGenericCreateFrame(Color backgroundColor); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/ImageFrameCollection{TPixel}.cs b/src/ImageSharp/ImageFrameCollection{TPixel}.cs new file mode 100644 index 0000000000..ffd7225bd4 --- /dev/null +++ b/src/ImageSharp/ImageFrameCollection{TPixel}.cs @@ -0,0 +1,358 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections; +using System.Collections.Generic; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp +{ + /// + /// Encapsulates a pixel-specific collection of instances + /// that make up an . + /// + /// The type of the pixel. + public sealed class ImageFrameCollection : ImageFrameCollection, IEnumerable> + where TPixel : struct, IPixel + { + private readonly IList> frames = new List>(); + private readonly Image parent; + + internal ImageFrameCollection(Image parent, int width, int height, TPixel backgroundColor) + { + this.parent = parent ?? throw new ArgumentNullException(nameof(parent)); + + // Frames are already cloned within the caller + this.frames.Add(new ImageFrame(parent.GetConfiguration(), width, height, backgroundColor)); + } + + internal ImageFrameCollection(Image parent, int width, int height, MemorySource memorySource) + { + this.parent = parent ?? throw new ArgumentNullException(nameof(parent)); + + // Frames are already cloned within the caller + this.frames.Add(new ImageFrame(parent.GetConfiguration(), width, height, memorySource)); + } + + internal ImageFrameCollection(Image parent, IEnumerable> frames) + { + Guard.NotNull(parent, nameof(parent)); + Guard.NotNull(frames, nameof(frames)); + + this.parent = parent; + + // Frames are already cloned by the caller + foreach (ImageFrame f in frames) + { + this.ValidateFrame(f); + this.frames.Add(f); + } + + // Ensure at least 1 frame was added to the frames collection + if (this.frames.Count == 0) + { + throw new ArgumentException("Must not be empty.", nameof(frames)); + } + } + + /// + /// Gets the number of frames. + /// + public override int Count => this.frames.Count; + + /// + /// Gets the root frame. + /// + public new ImageFrame RootFrame => this.frames.Count > 0 ? this.frames[0] : null; + + /// + protected override ImageFrame NonGenericRootFrame => this.RootFrame; + + /// + /// Gets the at the specified index. + /// + /// + /// The . + /// + /// The index. + /// The at the specified index. + public new ImageFrame this[int index] => this.frames[index]; + + /// + public override int IndexOf(ImageFrame frame) + { + return frame is ImageFrame specific ? this.IndexOf(specific) : -1; + } + + /// + /// Determines the index of a specific in the . + /// + /// The to locate in the . + /// The index of item if found in the list; otherwise, -1. + public int IndexOf(ImageFrame frame) => this.frames.IndexOf(frame); + + /// + /// Clones and inserts the into the at the specified . + /// + /// The zero-based index to insert the frame at. + /// The to clone and insert into the . + /// Frame must have the same dimensions as the image. + /// The cloned . + public ImageFrame InsertFrame(int index, ImageFrame source) + { + this.ValidateFrame(source); + ImageFrame clonedFrame = source.Clone(this.parent.GetConfiguration()); + this.frames.Insert(index, clonedFrame); + return clonedFrame; + } + + /// + /// Clones the frame and appends the clone to the end of the collection. + /// + /// The raw pixel data to generate the from. + /// The cloned . + public ImageFrame AddFrame(ImageFrame source) + { + this.ValidateFrame(source); + ImageFrame clonedFrame = source.Clone(this.parent.GetConfiguration()); + this.frames.Add(clonedFrame); + return clonedFrame; + } + + /// + /// Creates a new frame from the pixel data with the same dimensions as the other frames and inserts the + /// new frame at the end of the collection. + /// + /// The raw pixel data to generate the from. + /// The new . + public ImageFrame AddFrame(ReadOnlySpan source) + { + var frame = ImageFrame.LoadPixelData( + this.parent.GetConfiguration(), + source, + this.RootFrame.Width, + this.RootFrame.Height); + this.frames.Add(frame); + return frame; + } + + /// + /// Creates a new frame from the pixel data with the same dimensions as the other frames and inserts the + /// new frame at the end of the collection. + /// + /// The raw pixel data to generate the from. + /// The new . + public ImageFrame AddFrame(TPixel[] source) + { + Guard.NotNull(source, nameof(source)); + return this.AddFrame(source.AsSpan()); + } + + /// + /// Removes the frame at the specified index and frees all freeable resources associated with it. + /// + /// The zero-based index of the frame to remove. + /// Cannot remove last frame. + public override void RemoveFrame(int index) + { + if (index == 0 && this.Count == 1) + { + throw new InvalidOperationException("Cannot remove last frame."); + } + + ImageFrame frame = this.frames[index]; + this.frames.RemoveAt(index); + frame.Dispose(); + } + + /// + public override bool Contains(ImageFrame frame) => + frame is ImageFrame specific && this.Contains(specific); + + /// + /// Determines whether the contains the . + /// + /// The frame. + /// + /// true if the contains the specified frame; otherwise, false. + /// + public bool Contains(ImageFrame frame) => this.frames.Contains(frame); + + /// + /// Moves an from to . + /// + /// The zero-based index of the frame to move. + /// The index to move the frame to. + public override void MoveFrame(int sourceIndex, int destinationIndex) + { + if (sourceIndex == destinationIndex) + { + return; + } + + ImageFrame frameAtIndex = this.frames[sourceIndex]; + this.frames.RemoveAt(sourceIndex); + this.frames.Insert(destinationIndex, frameAtIndex); + } + + /// + /// Removes the frame at the specified index and creates a new image with only the removed frame + /// with the same metadata as the original image. + /// + /// The zero-based index of the frame to export. + /// Cannot remove last frame. + /// The new with the specified frame. + public new Image ExportFrame(int index) + { + ImageFrame frame = this[index]; + + if (this.Count == 1 && this.frames.Contains(frame)) + { + throw new InvalidOperationException("Cannot remove last frame."); + } + + this.frames.Remove(frame); + + return new Image(this.parent.GetConfiguration(), this.parent.Metadata.DeepClone(), new[] { frame }); + } + + /// + /// Creates an with only the frame at the specified index + /// with the same metadata as the original image. + /// + /// The zero-based index of the frame to clone. + /// The new with the specified frame. + public new Image CloneFrame(int index) + { + ImageFrame frame = this[index]; + ImageFrame clonedFrame = frame.Clone(); + return new Image(this.parent.GetConfiguration(), this.parent.Metadata.DeepClone(), new[] { clonedFrame }); + } + + /// + /// Creates a new and appends it to the end of the collection. + /// + /// + /// The new . + /// + public new ImageFrame CreateFrame() + { + var frame = new ImageFrame( + this.parent.GetConfiguration(), + this.RootFrame.Width, + this.RootFrame.Height); + this.frames.Add(frame); + return frame; + } + + /// + protected override IEnumerator NonGenericGetEnumerator() => this.frames.GetEnumerator(); + + /// + protected override ImageFrame NonGenericGetFrame(int index) => this[index]; + + /// + protected override ImageFrame NonGenericInsertFrame(int index, ImageFrame source) + { + Guard.NotNull(source, nameof(source)); + + if (source is ImageFrame compatibleSource) + { + return this.InsertFrame(index, compatibleSource); + } + + ImageFrame result = this.CopyNonCompatibleFrame(source); + this.frames.Insert(index, result); + return result; + } + + /// + protected override ImageFrame NonGenericAddFrame(ImageFrame source) + { + Guard.NotNull(source, nameof(source)); + + if (source is ImageFrame compatibleSource) + { + return this.AddFrame(compatibleSource); + } + + ImageFrame result = this.CopyNonCompatibleFrame(source); + this.frames.Add(result); + return result; + } + + /// + protected override Image NonGenericExportFrame(int index) => this.ExportFrame(index); + + /// + protected override Image NonGenericCloneFrame(int index) => this.CloneFrame(index); + + /// + protected override ImageFrame NonGenericCreateFrame(Color backgroundColor) => + this.CreateFrame(backgroundColor.ToPixel()); + + /// + protected override ImageFrame NonGenericCreateFrame() => this.CreateFrame(); + + /// + /// Creates a new and appends it to the end of the collection. + /// + /// The background color to initialize the pixels with. + /// + /// The new . + /// + public ImageFrame CreateFrame(TPixel backgroundColor) + { + var frame = new ImageFrame( + this.parent.GetConfiguration(), + this.RootFrame.Width, + this.RootFrame.Height, + backgroundColor); + this.frames.Add(frame); + return frame; + } + + /// + IEnumerator> IEnumerable>.GetEnumerator() => this.frames.GetEnumerator(); + + /// + IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)this.frames).GetEnumerator(); + + private void ValidateFrame(ImageFrame frame) + { + Guard.NotNull(frame, nameof(frame)); + + if (this.Count != 0) + { + if (this.RootFrame.Width != frame.Width || this.RootFrame.Height != frame.Height) + { + throw new ArgumentException("Frame must have the same dimensions as the image.", nameof(frame)); + } + } + } + + internal void Dispose() + { + foreach (ImageFrame f in this.frames) + { + f.Dispose(); + } + + this.frames.Clear(); + } + + private ImageFrame CopyNonCompatibleFrame(ImageFrame source) + { + ImageFrame result = new ImageFrame( + this.parent.GetConfiguration(), + source.Size(), + source.Metadata.DeepClone()); + source.CopyPixelsTo(result.PixelBuffer.Span); + return result; + } + } +} diff --git a/src/ImageSharp/ImageFrame{TPixel}.cs b/src/ImageSharp/ImageFrame{TPixel}.cs index 4f0be59713..982f8d8f29 100644 --- a/src/ImageSharp/ImageFrame{TPixel}.cs +++ b/src/ImageSharp/ImageFrame{TPixel}.cs @@ -3,6 +3,7 @@ using System; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; @@ -15,10 +16,12 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp { /// - /// Represents a single frame in a animation. + /// Represents a pixel-specific image frame containing all pixel data and . + /// In case of animated formats like gif, it contains the single frame in a animation. + /// In all other cases it is the only frame of the image. /// /// The pixel format. - public sealed class ImageFrame : IPixelSource, IDisposable + public sealed class ImageFrame : ImageFrame, IPixelSource, IDisposable where TPixel : struct, IPixel { private bool isDisposed; @@ -53,8 +56,12 @@ namespace SixLabors.ImageSharp /// The height of the image in pixels. /// The metadata. internal ImageFrame(Configuration configuration, int width, int height, ImageFrameMetadata metadata) - : this(configuration, width, height, default(TPixel), metadata) + : base(configuration, width, height, metadata) { + Guard.MustBeGreaterThan(width, 0, nameof(width)); + Guard.MustBeGreaterThan(height, 0, nameof(height)); + + this.PixelBuffer = this.MemoryAllocator.Allocate2D(width, height, AllocationOptions.Clean); } /// @@ -78,15 +85,12 @@ namespace SixLabors.ImageSharp /// The color to clear the image with. /// The metadata. internal ImageFrame(Configuration configuration, int width, int height, TPixel backgroundColor, ImageFrameMetadata metadata) + : base(configuration, width, height, metadata) { - Guard.NotNull(configuration, nameof(configuration)); Guard.MustBeGreaterThan(width, 0, nameof(width)); Guard.MustBeGreaterThan(height, 0, nameof(height)); - this.Configuration = configuration; - this.MemoryAllocator = configuration.MemoryAllocator; this.PixelBuffer = this.MemoryAllocator.Allocate2D(width, height); - this.Metadata = metadata ?? new ImageFrameMetadata(); this.Clear(configuration.GetParallelOptions(), backgroundColor); } @@ -111,16 +115,12 @@ namespace SixLabors.ImageSharp /// The memory source. /// The metadata. internal ImageFrame(Configuration configuration, int width, int height, MemorySource memorySource, ImageFrameMetadata metadata) + : base(configuration, width, height, metadata) { - Guard.NotNull(configuration, nameof(configuration)); Guard.MustBeGreaterThan(width, 0, nameof(width)); Guard.MustBeGreaterThan(height, 0, nameof(height)); - Guard.NotNull(metadata, nameof(metadata)); - this.Configuration = configuration; - this.MemoryAllocator = configuration.MemoryAllocator; this.PixelBuffer = new Buffer2D(memorySource, width, height); - this.Metadata = metadata; } /// @@ -129,27 +129,15 @@ namespace SixLabors.ImageSharp /// The configuration which allows altering default behaviour or extending the library. /// The source. internal ImageFrame(Configuration configuration, ImageFrame source) + : base(configuration, source.Width, source.Height, source.Metadata.DeepClone()) { Guard.NotNull(configuration, nameof(configuration)); Guard.NotNull(source, nameof(source)); - this.Configuration = configuration; - this.MemoryAllocator = configuration.MemoryAllocator; this.PixelBuffer = this.MemoryAllocator.Allocate2D(source.PixelBuffer.Width, source.PixelBuffer.Height); source.PixelBuffer.GetSpan().CopyTo(this.PixelBuffer.GetSpan()); - this.Metadata = source.Metadata.DeepClone(); } - /// - /// Gets the to use for buffer allocations. - /// - public MemoryAllocator MemoryAllocator { get; } - - /// - /// Gets the instance associated with this . - /// - internal Configuration Configuration { get; } - /// /// Gets the image pixels. Not private as Buffer2D requires an array in its constructor. /// @@ -158,21 +146,6 @@ namespace SixLabors.ImageSharp /// Buffer2D IPixelSource.PixelBuffer => this.PixelBuffer; - /// - /// Gets the width. - /// - public int Width => this.PixelBuffer.Width; - - /// - /// Gets the height. - /// - public int Height => this.PixelBuffer.Height; - - /// - /// Gets the metadata of the frame. - /// - public ImageFrameMetadata Metadata { get; } - /// /// Gets or sets the pixel at the specified position. /// @@ -188,18 +161,6 @@ namespace SixLabors.ImageSharp set => this.PixelBuffer[x, y] = value; } - /// - /// Gets the size of the frame. - /// - /// The - public Size Size() => new Size(this.Width, this.Height); - - /// - /// Gets the bounds of the frame. - /// - /// The - public Rectangle Bounds() => new Rectangle(0, 0, this.Width, this.Height); - /// /// Gets a reference to the pixel at the specified position. /// @@ -232,12 +193,13 @@ namespace SixLabors.ImageSharp Guard.NotNull(pixelSource, nameof(pixelSource)); Buffer2D.SwapOrCopyContent(this.PixelBuffer, pixelSource.PixelBuffer); + this.UpdateSize(this.PixelBuffer.Size()); } /// /// Disposes the object and frees resources for the Garbage Collector. /// - internal void Dispose() + public override void Dispose() { if (this.isDisposed) { @@ -251,6 +213,17 @@ namespace SixLabors.ImageSharp this.isDisposed = true; } + internal override void CopyPixelsTo(Span destination) + { + if (typeof(TPixel) == typeof(TDestinationPixel)) + { + Span dest1 = MemoryMarshal.Cast(destination); + this.PixelBuffer.Span.CopyTo(dest1); + } + + PixelOperations.Instance.To(this.Configuration, this.PixelBuffer.Span, destination); + } + /// public override string ToString() => $"ImageFrame<{typeof(TPixel).Name}>({this.Width}x{this.Height})"; @@ -325,8 +298,5 @@ namespace SixLabors.ImageSharp span.Fill(value); } } - - /// - void IDisposable.Dispose() => this.Dispose(); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index a3d614a067..3b02114349 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -5,6 +5,16 @@ SixLabors.ImageSharp SixLabors.ImageSharp A cross-platform library for the processing of image files; written in C# + en + + $(packageversion) + 0.0.1 + + netcoreapp2.1;netstandard1.3;netstandard2.0;net472 + + true + true + SixLabors.ImageSharp SixLabors.ImageSharp Image Resize Crop Gif Jpg Jpeg Bitmap Png Core SixLabors.ImageSharp diff --git a/src/ImageSharp/Image{TPixel}.cs b/src/ImageSharp/Image{TPixel}.cs index 2b93b710b0..5d0364dd87 100644 --- a/src/ImageSharp/Image{TPixel}.cs +++ b/src/ImageSharp/Image{TPixel}.cs @@ -80,7 +80,12 @@ namespace SixLabors.ImageSharp /// The width of the image in pixels. /// The height of the image in pixels. /// The images metadata. - internal Image(Configuration configuration, MemorySource memorySource, int width, int height, ImageMetadata metadata) + internal Image( + Configuration configuration, + MemorySource memorySource, + int width, + int height, + ImageMetadata metadata) : base(configuration, PixelTypeInfo.Create(), metadata, width, height) { this.Frames = new ImageFrameCollection(this, width, height, memorySource); @@ -95,7 +100,12 @@ namespace SixLabors.ImageSharp /// The height of the image in pixels. /// The color to initialize the pixels with. /// The images metadata. - internal Image(Configuration configuration, int width, int height, TPixel backgroundColor, ImageMetadata metadata) + internal Image( + Configuration configuration, + int width, + int height, + TPixel backgroundColor, + ImageMetadata metadata) : base(configuration, PixelTypeInfo.Create(), metadata, width, height) { this.Frames = new ImageFrameCollection(this, width, height, backgroundColor); @@ -114,10 +124,13 @@ namespace SixLabors.ImageSharp this.Frames = new ImageFrameCollection(this, frames); } + /// + protected override ImageFrameCollection NonGenericFrameCollection => this.Frames; + /// /// Gets the frames. /// - public ImageFrameCollection Frames { get; } + public new ImageFrameCollection Frames { get; } /// /// Gets the root frame. @@ -149,7 +162,8 @@ namespace SixLabors.ImageSharp /// Returns a new with all the same pixel data as the original. public Image Clone(Configuration configuration) { - IEnumerable> clonedFrames = this.Frames.Select(x => x.Clone(configuration)); + IEnumerable> clonedFrames = + this.Frames.Select, ImageFrame>(x => x.Clone(configuration)); return new Image(configuration, this.Metadata.DeepClone(), clonedFrames); } @@ -161,7 +175,8 @@ namespace SixLabors.ImageSharp /// The . public override Image CloneAs(Configuration configuration) { - IEnumerable> clonedFrames = this.Frames.Select(x => x.CloneAs(configuration)); + IEnumerable> clonedFrames = + this.Frames.Select, ImageFrame>(x => x.CloneAs(configuration)); return new Image(configuration, this.Metadata.DeepClone(), clonedFrames); } @@ -214,4 +229,4 @@ namespace SixLabors.ImageSharp return rootSize; } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.cs index 4cff1f8c10..8481fc62c9 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.cs @@ -11,6 +11,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; + namespace SixLabors.ImageSharp.PixelFormats { /// @@ -222,6 +223,15 @@ namespace SixLabors.ImageSharp.PixelFormats dp.FromArgb32(sp); } } + /// + internal override void From( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + PixelOperations.Instance.ToArgb32(configuration, sourcePixels, destinationPixels); + } + } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.tt b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.tt index 0a58504e15..3a4c7b459d 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.tt @@ -16,4 +16,4 @@ namespace SixLabors.ImageSharp.PixelFormats <# GenerateAllDefaultConversionMethods("Argb32"); #> } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.cs index 225e4b5e01..4b2b3a02e4 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.cs @@ -11,6 +11,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; + namespace SixLabors.ImageSharp.PixelFormats { /// @@ -196,6 +197,15 @@ namespace SixLabors.ImageSharp.PixelFormats dp.FromBgr24(sp); } } + /// + internal override void From( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + PixelOperations.Instance.ToBgr24(configuration, sourcePixels, destinationPixels); + } + } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.tt b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.tt index 84b89aa32c..cfaefeda9c 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.tt @@ -16,4 +16,4 @@ namespace SixLabors.ImageSharp.PixelFormats <# GenerateAllDefaultConversionMethods("Bgr24"); #> } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.cs index ce049115ea..95d85c7c8d 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.cs @@ -11,6 +11,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; + namespace SixLabors.ImageSharp.PixelFormats { /// @@ -222,6 +223,15 @@ namespace SixLabors.ImageSharp.PixelFormats dp.FromBgra32(sp); } } + /// + internal override void From( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + PixelOperations.Instance.ToBgra32(configuration, sourcePixels, destinationPixels); + } + } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.tt b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.tt index 004ceff51e..58ecfa5a6c 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.tt @@ -16,4 +16,4 @@ namespace SixLabors.ImageSharp.PixelFormats <# GenerateAllDefaultConversionMethods("Bgra32"); #> } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray16.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray16.PixelOperations.Generated.cs index 288c5d92e4..e6ed75d49a 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray16.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray16.PixelOperations.Generated.cs @@ -11,6 +11,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; + namespace SixLabors.ImageSharp.PixelFormats { /// @@ -185,6 +186,15 @@ namespace SixLabors.ImageSharp.PixelFormats dp.FromGray16(sp); } } + /// + internal override void From( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + PixelOperations.Instance.ToGray16(configuration, sourcePixels, destinationPixels); + } + } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray16.PixelOperations.Generated.tt b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray16.PixelOperations.Generated.tt index 3cbc01e88c..ac484bdbc0 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray16.PixelOperations.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray16.PixelOperations.Generated.tt @@ -16,4 +16,4 @@ namespace SixLabors.ImageSharp.PixelFormats <# GenerateAllDefaultConversionMethods("Gray16"); #> } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray8.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray8.PixelOperations.Generated.cs index f7e94788e3..c9a3a06fe6 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray8.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray8.PixelOperations.Generated.cs @@ -11,6 +11,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; + namespace SixLabors.ImageSharp.PixelFormats { /// @@ -185,6 +186,15 @@ namespace SixLabors.ImageSharp.PixelFormats dp.FromGray8(sp); } } + /// + internal override void From( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + PixelOperations.Instance.ToGray8(configuration, sourcePixels, destinationPixels); + } + } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray8.PixelOperations.Generated.tt b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray8.PixelOperations.Generated.tt index d35843ccda..ffa15af9dc 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray8.PixelOperations.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray8.PixelOperations.Generated.tt @@ -16,4 +16,4 @@ namespace SixLabors.ImageSharp.PixelFormats <# GenerateAllDefaultConversionMethods("Gray8"); #> } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs index 02a8869ecc..ef224c02dc 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs @@ -11,6 +11,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; + namespace SixLabors.ImageSharp.PixelFormats { /// @@ -196,6 +197,15 @@ namespace SixLabors.ImageSharp.PixelFormats dp.FromRgb24(sp); } } + /// + internal override void From( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + PixelOperations.Instance.ToRgb24(configuration, sourcePixels, destinationPixels); + } + } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.tt b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.tt index d96c3684b5..fc149b2380 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.tt @@ -16,4 +16,4 @@ namespace SixLabors.ImageSharp.PixelFormats <# GenerateAllDefaultConversionMethods("Rgb24"); #> } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb48.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb48.PixelOperations.Generated.cs index 30c9972bbf..6943888992 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb48.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb48.PixelOperations.Generated.cs @@ -11,6 +11,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; + namespace SixLabors.ImageSharp.PixelFormats { /// @@ -185,6 +186,15 @@ namespace SixLabors.ImageSharp.PixelFormats dp.FromRgb48(sp); } } + /// + internal override void From( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + PixelOperations.Instance.ToRgb48(configuration, sourcePixels, destinationPixels); + } + } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb48.PixelOperations.Generated.tt b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb48.PixelOperations.Generated.tt index 7bff336386..957c8f886c 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb48.PixelOperations.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb48.PixelOperations.Generated.tt @@ -16,4 +16,4 @@ namespace SixLabors.ImageSharp.PixelFormats <# GenerateAllDefaultConversionMethods("Rgb48"); #> } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba32.PixelOperations.Generated.cs index da2ce3770b..f0c8bc8842 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba32.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba32.PixelOperations.Generated.cs @@ -11,6 +11,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; + namespace SixLabors.ImageSharp.PixelFormats { /// @@ -211,6 +212,15 @@ namespace SixLabors.ImageSharp.PixelFormats dp.FromRgba32(sp); } } + /// + internal override void From( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + PixelOperations.Instance.ToRgba32(configuration, sourcePixels, destinationPixels); + } + } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba32.PixelOperations.Generated.tt b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba32.PixelOperations.Generated.tt index 6b9e2d1248..905e2cc58f 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba32.PixelOperations.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba32.PixelOperations.Generated.tt @@ -16,4 +16,4 @@ namespace SixLabors.ImageSharp.PixelFormats <# GenerateAllDefaultConversionMethods("Rgba32"); #> } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba64.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba64.PixelOperations.Generated.cs index 42c40ad5d7..0faa6eb44d 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba64.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba64.PixelOperations.Generated.cs @@ -11,6 +11,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; + namespace SixLabors.ImageSharp.PixelFormats { /// @@ -185,6 +186,15 @@ namespace SixLabors.ImageSharp.PixelFormats dp.FromRgba64(sp); } } + /// + internal override void From( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + PixelOperations.Instance.ToRgba64(configuration, sourcePixels, destinationPixels); + } + } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba64.PixelOperations.Generated.tt b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba64.PixelOperations.Generated.tt index d15945f947..03179a3920 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba64.PixelOperations.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba64.PixelOperations.Generated.tt @@ -16,4 +16,4 @@ namespace SixLabors.ImageSharp.PixelFormats <# GenerateAllDefaultConversionMethods("Rgba64"); #> } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/_Common.ttinclude b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/_Common.ttinclude index 584615532d..17e9469f31 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/_Common.ttinclude +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/_Common.ttinclude @@ -22,6 +22,19 @@ using System.Runtime.InteropServices; // Types with Rgba32-combatible to/from Vector4 conversion static readonly string[] Rgba32CompatibleTypes = { "Argb32", "Bgra32", "Rgb24", "Bgr24" }; + void GenerateGenericConverterMethods(string pixelType) + { +#> + /// + internal override void From( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span<<#=pixelType#>> destinationPixels) + { + PixelOperations.Instance.To<#=pixelType#>(configuration, sourcePixels, destinationPixels); + } +<#+ + } void GenerateDefaultSelfConversionMethods(string pixelType) { @@ -117,7 +130,7 @@ using System.Runtime.InteropServices; } #> /// - internal override void FromVector4(Configuration configuration, Span sourceVectors, Span<<#=pixelType#>> destPixels, PixelConversionModifiers modifiers) + internal override void FromVector4Destructive(Configuration configuration, Span sourceVectors, Span<<#=pixelType#>> destPixels, PixelConversionModifiers modifiers) { Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, modifiers.Remove(<#=removeTheseModifiers#>)); } @@ -156,5 +169,7 @@ using System.Runtime.InteropServices; { GenerateDefaultConvertToMethod(pixelType, destPixelType); } + + GenerateGenericConverterMethods(pixelType); } #> diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Gray16.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Gray16.cs index 6fce1c7575..195a8bedb0 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Gray16.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Gray16.cs @@ -54,7 +54,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + public void FromScaledVector4(Vector4 vector) => this.ConvertFromRgbaScaledVector4(vector); /// [MethodImpl(InliningOptions.ShortMethod)] @@ -62,11 +62,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) - { - vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One) * Max * Average; - this.PackedValue = (ushort)MathF.Round(vector.X + vector.Y + vector.Z); - } + public void FromVector4(Vector4 vector) => this.ConvertFromRgbaScaledVector4(vector); /// [MethodImpl(InliningOptions.ShortMethod)] @@ -176,9 +172,9 @@ namespace SixLabors.ImageSharp.PixelFormats { vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One) * Max; this.PackedValue = ImageMaths.Get16BitBT709Luminance( - (ushort)MathF.Round(vector.X), - (ushort)MathF.Round(vector.Y), - (ushort)MathF.Round(vector.Z)); + vector.X, + vector.Y, + vector.Z); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Gray8.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Gray8.cs index 1c278b434c..09597a949f 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Gray8.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Gray8.cs @@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + public void FromScaledVector4(Vector4 vector) => this.ConvertFromRgbaScaledVector4(vector); /// [MethodImpl(InliningOptions.ShortMethod)] @@ -67,15 +67,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) - { - vector = Vector4.Max(Min, vector); - vector = Vector4.Min(Max, vector); - - float roundedSum = Vector4.Dot(vector, Accumulator); - - this.PackedValue = (byte)roundedSum; - } + public void FromVector4(Vector4 vector) => this.ConvertFromRgbaScaledVector4(vector); /// [MethodImpl(InliningOptions.ShortMethod)] @@ -166,4 +158,4 @@ namespace SixLabors.ImageSharp.PixelFormats this.PackedValue = ImageMaths.Get8BitBT709Luminance((byte)vector.X, (byte)vector.Y, (byte)vector.Z); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.PixelOperations.cs index 3112126e76..b331dbd99f 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.PixelOperations.cs @@ -3,6 +3,7 @@ using System; using System.Numerics; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.PixelFormats.Utils; @@ -44,6 +45,38 @@ namespace SixLabors.ImageSharp.PixelFormats MemoryMarshal.Cast(sourcePixels).CopyTo(destVectors); Vector4Converters.ApplyForwardConversionModifiers(destVectors, modifiers); } + + internal override void ToGray8(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Vector4 sourceBaseRef = ref Unsafe.As(ref MemoryMarshal.GetReference(sourcePixels)); + ref Gray8 destBaseRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Vector4 sp = ref Unsafe.Add(ref sourceBaseRef, i); + ref Gray8 dp = ref Unsafe.Add(ref destBaseRef, i); + + dp.ConvertFromRgbaScaledVector4(sp); + } + } + + internal override void ToGray16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Vector4 sourceBaseRef = ref Unsafe.As(ref MemoryMarshal.GetReference(sourcePixels)); + ref Gray16 destBaseRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Vector4 sp = ref Unsafe.Add(ref sourceBaseRef, i); + ref Gray16 dp = ref Unsafe.Add(ref destBaseRef, i); + + dp.ConvertFromRgbaScaledVector4(sp); + } + } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs index d009822e80..2b2d79c732 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs @@ -7,6 +7,8 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Memory; + namespace SixLabors.ImageSharp.PixelFormats { /// @@ -89,63 +91,56 @@ namespace SixLabors.ImageSharp.PixelFormats Span destVectors) => this.ToVector4(configuration, sourcePixels, destVectors, PixelConversionModifiers.None); - /// - /// Converts 'sourceColors.Length' pixels from 'sourceColors' into 'destinationColors'. - /// - /// The destination pixel type. - /// A to configure internal operations - /// The to the source colors. - /// The to the destination colors. - internal virtual void To( + internal virtual void From( Configuration configuration, - ReadOnlySpan sourceColors, - Span destinationColors) - where TDestinationPixel : struct, IPixel + ReadOnlySpan sourcePixels, + Span destinationPixels) + where TSourcePixel : struct, IPixel { - Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourceColors, destinationColors, nameof(destinationColors)); - - int count = sourceColors.Length; - ref TPixel sourceRef = ref MemoryMarshal.GetReference(sourceColors); - - // Gray8 and Gray16 are special implementations of IPixel in that they do not conform to the - // standard RGBA colorspace format and must be converted from RGBA using the special ITU BT709 algorithm. - // One of the requirements of FromScaledVector4/ToScaledVector4 is that it unaware of this and - // packs/unpacks the pixel without and conversion so we employ custom methods do do this. - if (typeof(TDestinationPixel) == typeof(Gray16)) + const int SliceLength = 1024; + int numberOfSlices = sourcePixels.Length / SliceLength; + using (IMemoryOwner tempVectors = configuration.MemoryAllocator.Allocate(SliceLength)) { - ref Gray16 gray16Ref = ref MemoryMarshal.GetReference(MemoryMarshal.Cast(destinationColors)); - for (int i = 0; i < count; i++) + Span vectorSpan = tempVectors.GetSpan(); + for (int i = 0; i < numberOfSlices; i++) { - ref TPixel sp = ref Unsafe.Add(ref sourceRef, i); - ref Gray16 dp = ref Unsafe.Add(ref gray16Ref, i); - dp.ConvertFromRgbaScaledVector4(sp.ToScaledVector4()); + int start = i * SliceLength; + ReadOnlySpan s = sourcePixels.Slice(start, SliceLength); + Span d = destinationPixels.Slice(start, SliceLength); + PixelOperations.Instance.ToVector4(configuration, s, vectorSpan); + this.FromVector4Destructive(configuration, vectorSpan, d); } - return; - } - - if (typeof(TDestinationPixel) == typeof(Gray8)) - { - ref Gray8 gray8Ref = ref MemoryMarshal.GetReference(MemoryMarshal.Cast(destinationColors)); - for (int i = 0; i < count; i++) + int endOfCompleteSlices = numberOfSlices * SliceLength; + int remainder = sourcePixels.Length - endOfCompleteSlices; + if (remainder > 0) { - ref TPixel sp = ref Unsafe.Add(ref sourceRef, i); - ref Gray8 dp = ref Unsafe.Add(ref gray8Ref, i); - dp.ConvertFromRgbaScaledVector4(sp.ToScaledVector4()); + ReadOnlySpan s = sourcePixels.Slice(endOfCompleteSlices); + Span d = destinationPixels.Slice(endOfCompleteSlices); + vectorSpan = vectorSpan.Slice(0, remainder); + PixelOperations.Instance.ToVector4(configuration, s, vectorSpan); + this.FromVector4Destructive(configuration, vectorSpan, d); } - - return; } + } - // Normal conversion - ref TDestinationPixel destRef = ref MemoryMarshal.GetReference(destinationColors); - for (int i = 0; i < count; i++) - { - ref TPixel sp = ref Unsafe.Add(ref sourceRef, i); - ref TDestinationPixel dp = ref Unsafe.Add(ref destRef, i); - dp.FromScaledVector4(sp.ToScaledVector4()); - } + /// + /// Converts 'sourcePixels.Length' pixels from 'sourcePixels' into 'destinationPixels'. + /// + /// The destination pixel type. + /// A to configure internal operations. + /// The to the source pixels. + /// The to the destination pixels. + internal virtual void To( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + where TDestinationPixel : struct, IPixel + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + + PixelOperations.Instance.From(configuration, sourcePixels, destinationPixels); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs index 9625ca216e..529a49bf7a 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs @@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms protected override Image CreateDestination(Image source, Rectangle sourceRectangle) { // We will always be creating the clone even for mutate because we may need to resize the canvas - IEnumerable> frames = source.Frames.Select( + IEnumerable> frames = source.Frames.Select, ImageFrame>( x => new ImageFrame(source.GetConfiguration(), this.TargetDimensions, x.Metadata.DeepClone())); // Use the overload to prevent an extra frame being added diff --git a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs index 4c4c27defa..25f8d78491 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs @@ -36,7 +36,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms protected override Image CreateDestination(Image source, Rectangle sourceRectangle) { // We will always be creating the clone even for mutate because we may need to resize the canvas - IEnumerable> frames = source.Frames.Select(x => new ImageFrame(source.GetConfiguration(), this.CropRectangle.Width, this.CropRectangle.Height, x.Metadata.DeepClone())); + IEnumerable> frames = source.Frames.Select, ImageFrame>( + x => new ImageFrame( + source.GetConfiguration(), + this.CropRectangle.Width, + this.CropRectangle.Height, + x.Metadata.DeepClone())); // Use the overload to prevent an extra frame being added return new Image(source.GetConfiguration(), source.Metadata.DeepClone(), frames); diff --git a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs index 15d8141baa..80b03000b8 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs @@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms protected override Image CreateDestination(Image source, Rectangle sourceRectangle) { // We will always be creating the clone even for mutate because we may need to resize the canvas - IEnumerable> frames = source.Frames.Select( + IEnumerable> frames = source.Frames.Select, ImageFrame>( x => new ImageFrame( source.GetConfiguration(), this.TargetDimensions.Width, @@ -140,4 +140,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs index fdd1d339dd..fb03057a48 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs @@ -68,7 +68,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms protected override Image CreateDestination(Image source, Rectangle sourceRectangle) { // We will always be creating the clone even for mutate because we may need to resize the canvas - IEnumerable> frames = source.Frames.Select(x => new ImageFrame(source.GetConfiguration(), this.Width, this.Height, x.Metadata.DeepClone())); + IEnumerable> frames = source.Frames.Select, ImageFrame>( + x => new ImageFrame( + source.GetConfiguration(), + this.Width, + this.Height, + x.Metadata.DeepClone())); // Use the overload to prevent an extra frame being added return new Image(source.GetConfiguration(), source.Metadata.DeepClone(), frames); diff --git a/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs b/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs new file mode 100644 index 0000000000..37c3a27cd0 --- /dev/null +++ b/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs @@ -0,0 +1,346 @@ +// // Copyright (c) Six Labors and contributors. +// // Licensed under the Apache License, Version 2.0. + +using System; +using System.Linq; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests +{ + public abstract partial class ImageFrameCollectionTests + { + [GroupOutput("ImageFramesCollectionTests")] + public class Generic : ImageFrameCollectionTests + { + [Fact] + public void Constructor_ShouldCreateOneFrame() + { + Assert.Equal(1, this.Collection.Count); + } + + [Fact] + public void AddNewFrame_FramesMustHaveSameSize() + { + ArgumentException ex = Assert.Throws( + () => + { + this.Collection.AddFrame(new ImageFrame(Configuration.Default, 1, 1)); + }); + + Assert.StartsWith("Frame must have the same dimensions as the image.", ex.Message); + } + + [Fact] + public void AddNewFrame_Frame_FramesNotBeNull() + { + ArgumentNullException ex = Assert.Throws( + () => + { + this.Collection.AddFrame((ImageFrame)null); + }); + + Assert.StartsWith("Value cannot be null.", ex.Message); + } + + [Fact] + public void AddNewFrame_PixelBuffer_DataMustNotBeNull() + { + Rgba32[] data = null; + + ArgumentNullException ex = Assert.Throws( + () => + { + this.Collection.AddFrame(data); + }); + + Assert.StartsWith("Value cannot be null.", ex.Message); + } + + [Fact] + public void AddNewFrame_PixelBuffer_BufferIncorrectSize() + { + ArgumentOutOfRangeException ex = Assert.Throws( + () => + { + this.Collection.AddFrame(new Rgba32[0]); + }); + + Assert.StartsWith("Value 0 must be greater than or equal to 100.", ex.Message); + } + + [Fact] + public void InsertNewFrame_FramesMustHaveSameSize() + { + ArgumentException ex = Assert.Throws( + () => + { + this.Collection.InsertFrame(1, new ImageFrame(Configuration.Default, 1, 1)); + }); + + Assert.StartsWith("Frame must have the same dimensions as the image.", ex.Message); + } + + [Fact] + public void InsertNewFrame_FramesNotBeNull() + { + ArgumentNullException ex = Assert.Throws( + () => + { + this.Collection.InsertFrame(1, null); + }); + + Assert.StartsWith("Value cannot be null.", ex.Message); + } + + [Fact] + public void Constructor_FramesMustHaveSameSize() + { + ArgumentException ex = Assert.Throws( + () => + { + var collection = new ImageFrameCollection( + this.Image, + new[] + { + new ImageFrame(Configuration.Default, 10, 10), + new ImageFrame(Configuration.Default, 1, 1) + }); + }); + + Assert.StartsWith("Frame must have the same dimensions as the image.", ex.Message); + } + + [Fact] + public void RemoveAtFrame_ThrowIfRemovingLastFrame() + { + var collection = new ImageFrameCollection( + this.Image, + new[] { new ImageFrame(Configuration.Default, 10, 10) }); + + InvalidOperationException ex = Assert.Throws( + () => + { + collection.RemoveFrame(0); + }); + Assert.Equal("Cannot remove last frame.", ex.Message); + } + + [Fact] + public void RemoveAtFrame_CanRemoveFrameZeroIfMultipleFramesExist() + { + var collection = new ImageFrameCollection( + this.Image, + new[] + { + new ImageFrame(Configuration.Default, 10, 10), + new ImageFrame(Configuration.Default, 10, 10) + }); + + collection.RemoveFrame(0); + Assert.Equal(1, collection.Count); + } + + [Fact] + public void RootFrameIsFrameAtIndexZero() + { + var collection = new ImageFrameCollection( + this.Image, + new[] + { + new ImageFrame(Configuration.Default, 10, 10), + new ImageFrame(Configuration.Default, 10, 10) + }); + + Assert.Equal(collection.RootFrame, collection[0]); + } + + [Fact] + public void ConstructorPopulatesFrames() + { + var collection = new ImageFrameCollection( + this.Image, + new[] + { + new ImageFrame(Configuration.Default, 10, 10), + new ImageFrame(Configuration.Default, 10, 10) + }); + + Assert.Equal(2, collection.Count); + } + + [Fact] + public void DisposeClearsCollection() + { + var collection = new ImageFrameCollection( + this.Image, + new[] + { + new ImageFrame(Configuration.Default, 10, 10), + new ImageFrame(Configuration.Default, 10, 10) + }); + + collection.Dispose(); + + Assert.Equal(0, collection.Count); + } + + [Fact] + public void Dispose_DisposesAllInnerFrames() + { + var collection = new ImageFrameCollection( + this.Image, + new[] + { + new ImageFrame(Configuration.Default, 10, 10), + new ImageFrame(Configuration.Default, 10, 10) + }); + + IPixelSource[] framesSnapShot = collection.OfType>().ToArray(); + collection.Dispose(); + + Assert.All( + framesSnapShot, + f => + { + // the pixel source of the frame is null after its been disposed. + Assert.Null(f.PixelBuffer); + }); + } + + [Theory] + [WithTestPatternImages(10, 10, PixelTypes.Rgba32)] + public void CloneFrame(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image img = provider.GetImage()) + { + img.Frames.AddFrame(new ImageFrame(Configuration.Default, 10, 10)); // add a frame anyway + using (Image cloned = img.Frames.CloneFrame(0)) + { + Assert.Equal(2, img.Frames.Count); + cloned.ComparePixelBufferTo(img.GetPixelSpan()); + } + } + } + + [Theory] + [WithTestPatternImages(10, 10, PixelTypes.Rgba32)] + public void ExtractFrame(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image img = provider.GetImage()) + { + var sourcePixelData = img.GetPixelSpan().ToArray(); + + img.Frames.AddFrame(new ImageFrame(Configuration.Default, 10, 10)); + using (Image cloned = img.Frames.ExportFrame(0)) + { + Assert.Equal(1, img.Frames.Count); + cloned.ComparePixelBufferTo(sourcePixelData); + } + } + } + + [Fact] + public void CreateFrame_Default() + { + this.Image.Frames.CreateFrame(); + + Assert.Equal(2, this.Image.Frames.Count); + this.Image.Frames[1].ComparePixelBufferTo(default(Rgba32)); + } + + [Fact] + public void CreateFrame_CustomFillColor() + { + this.Image.Frames.CreateFrame(Rgba32.HotPink); + + Assert.Equal(2, this.Image.Frames.Count); + this.Image.Frames[1].ComparePixelBufferTo(Rgba32.HotPink); + } + + [Fact] + public void AddFrameFromPixelData() + { + var pixelData = this.Image.Frames.RootFrame.GetPixelSpan().ToArray(); + this.Image.Frames.AddFrame(pixelData); + Assert.Equal(2, this.Image.Frames.Count); + } + + [Fact] + public void AddFrame_clones_sourceFrame() + { + var pixelData = this.Image.Frames.RootFrame.GetPixelSpan().ToArray(); + var otherFRame = new ImageFrame(Configuration.Default, 10, 10); + var addedFrame = this.Image.Frames.AddFrame(otherFRame); + addedFrame.ComparePixelBufferTo(otherFRame.GetPixelSpan()); + Assert.NotEqual(otherFRame, addedFrame); + } + + [Fact] + public void InsertFrame_clones_sourceFrame() + { + var pixelData = this.Image.Frames.RootFrame.GetPixelSpan().ToArray(); + var otherFRame = new ImageFrame(Configuration.Default, 10, 10); + var addedFrame = this.Image.Frames.InsertFrame(0, otherFRame); + addedFrame.ComparePixelBufferTo(otherFRame.GetPixelSpan()); + Assert.NotEqual(otherFRame, addedFrame); + } + + [Fact] + public void MoveFrame_LeavesFrmaeInCorrectLocation() + { + for (var i = 0; i < 9; i++) + { + this.Image.Frames.CreateFrame(); + } + + var frame = this.Image.Frames[4]; + this.Image.Frames.MoveFrame(4, 7); + var newIndex = this.Image.Frames.IndexOf(frame); + Assert.Equal(7, newIndex); + } + + [Fact] + public void IndexOf_ReturnsCorrectIndex() + { + for (var i = 0; i < 9; i++) + { + this.Image.Frames.CreateFrame(); + } + + var frame = this.Image.Frames[4]; + var index = this.Image.Frames.IndexOf(frame); + Assert.Equal(4, index); + } + + [Fact] + public void Contains_TrueIfMember() + { + for (var i = 0; i < 9; i++) + { + this.Image.Frames.CreateFrame(); + } + + var frame = this.Image.Frames[4]; + Assert.True(this.Image.Frames.Contains(frame)); + } + + [Fact] + public void Contains_FalseIfNonMember() + { + for (var i = 0; i < 9; i++) + { + this.Image.Frames.CreateFrame(); + } + + var frame = new ImageFrame(Configuration.Default, 10, 10); + Assert.False(this.Image.Frames.Contains(frame)); + } + } + } +} diff --git a/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.NonGeneric.cs b/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.NonGeneric.cs new file mode 100644 index 0000000000..0956cce9c4 --- /dev/null +++ b/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.NonGeneric.cs @@ -0,0 +1,325 @@ +// // Copyright (c) Six Labors and contributors. +// // Licensed under the Apache License, Version 2.0. + +using System; +using System.Drawing.Imaging; +using System.Linq; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.PixelFormats; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests +{ + public abstract partial class ImageFrameCollectionTests + { + [GroupOutput("ImageFramesCollectionTests")] + public class NonGeneric : ImageFrameCollectionTests + { + private new Image Image => base.Image; + + private new ImageFrameCollection Collection => base.Collection; + + [Fact] + public void AddFrame_OfDifferentPixelType() + { + using (Image sourceImage = new Image( + this.Image.GetConfiguration(), + this.Image.Width, + this.Image.Height, + (Bgra32)Color.Blue)) + { + this.Collection.AddFrame(sourceImage.Frames.RootFrame); + } + + Rgba32[] expectedAllBlue = + Enumerable.Repeat(Rgba32.Blue, this.Image.Width * this.Image.Height).ToArray(); + + Assert.Equal(2, this.Collection.Count); + ImageFrame actualFrame = (ImageFrame)this.Collection[1]; + + actualFrame.ComparePixelBufferTo(expectedAllBlue); + } + + [Fact] + public void InsertFrame_OfDifferentPixelType() + { + using (Image sourceImage = new Image( + this.Image.GetConfiguration(), + this.Image.Width, + this.Image.Height, + (Bgra32)Color.Blue)) + { + this.Collection.InsertFrame(0, sourceImage.Frames.RootFrame); + } + + Rgba32[] expectedAllBlue = + Enumerable.Repeat(Rgba32.Blue, this.Image.Width * this.Image.Height).ToArray(); + + Assert.Equal(2, this.Collection.Count); + ImageFrame actualFrame = (ImageFrame)this.Collection[0]; + + actualFrame.ComparePixelBufferTo(expectedAllBlue); + + } + + [Fact] + public void Constructor_ShouldCreateOneFrame() + { + Assert.Equal(1, this.Collection.Count); + } + + [Fact] + public void AddNewFrame_FramesMustHaveSameSize() + { + ArgumentException ex = Assert.Throws( + () => + { + this.Collection.AddFrame(new ImageFrame(Configuration.Default, 1, 1)); + }); + + Assert.StartsWith("Frame must have the same dimensions as the image.", ex.Message); + } + + [Fact] + public void AddNewFrame_Frame_FramesNotBeNull() + { + ArgumentNullException ex = Assert.Throws( + () => + { + this.Collection.AddFrame((ImageFrame)null); + }); + + Assert.StartsWith("Value cannot be null.", ex.Message); + } + + [Fact] + public void InsertNewFrame_FramesMustHaveSameSize() + { + ArgumentException ex = Assert.Throws( + () => + { + this.Collection.InsertFrame(1, new ImageFrame(Configuration.Default, 1, 1)); + }); + + Assert.StartsWith("Frame must have the same dimensions as the image.", ex.Message); + } + + [Fact] + public void InsertNewFrame_FramesNotBeNull() + { + ArgumentNullException ex = Assert.Throws( + () => + { + this.Collection.InsertFrame(1, null); + }); + + Assert.StartsWith("Value cannot be null.", ex.Message); + } + + + [Fact] + public void RemoveAtFrame_ThrowIfRemovingLastFrame() + { + + InvalidOperationException ex = Assert.Throws( + () => + { + this.Collection.RemoveFrame(0); + }); + Assert.Equal("Cannot remove last frame.", ex.Message); + } + + [Fact] + public void RemoveAtFrame_CanRemoveFrameZeroIfMultipleFramesExist() + { + this.Collection.AddFrame(new ImageFrame(Configuration.Default, 10, 10)); + + this.Collection.RemoveFrame(0); + Assert.Equal(1, this.Collection.Count); + } + + [Fact] + public void RootFrameIsFrameAtIndexZero() + { + Assert.Equal(this.Collection.RootFrame, this.Collection[0]); + } + + [Theory] + [WithTestPatternImages(10, 10, PixelTypes.Rgba32 | PixelTypes.Bgr24)] + public void CloneFrame(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image img = provider.GetImage()) + { + ImageFrameCollection nonGenericFrameCollection = img.Frames; + + nonGenericFrameCollection.AddFrame(new ImageFrame(Configuration.Default, 10, 10)); // add a frame anyway + using (Image cloned = nonGenericFrameCollection.CloneFrame(0)) + { + Assert.Equal(2, img.Frames.Count); + + Image expectedClone = (Image)cloned; + + expectedClone.ComparePixelBufferTo(img.GetPixelSpan()); + } + } + } + + [Theory] + [WithTestPatternImages(10, 10, PixelTypes.Rgba32)] + public void ExtractFrame(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image img = provider.GetImage()) + { + var sourcePixelData = img.GetPixelSpan().ToArray(); + + ImageFrameCollection nonGenericFrameCollection = img.Frames; + + nonGenericFrameCollection.AddFrame(new ImageFrame(Configuration.Default, 10, 10)); + using (Image cloned = nonGenericFrameCollection.ExportFrame(0)) + { + Assert.Equal(1, img.Frames.Count); + + Image expectedClone = (Image)cloned; + expectedClone.ComparePixelBufferTo(sourcePixelData); + } + } + } + + [Fact] + public void CreateFrame_Default() + { + this.Image.Frames.CreateFrame(); + + Assert.Equal(2, this.Image.Frames.Count); + + ImageFrame frame = (ImageFrame)this.Image.Frames[1]; + + frame.ComparePixelBufferTo(default(Rgba32)); + } + + [Fact] + public void CreateFrame_CustomFillColor() + { + this.Image.Frames.CreateFrame(Rgba32.HotPink); + + Assert.Equal(2, this.Image.Frames.Count); + + ImageFrame frame = (ImageFrame)this.Image.Frames[1]; + + frame.ComparePixelBufferTo(Rgba32.HotPink); + } + + [Fact] + public void MoveFrame_LeavesFrmaeInCorrectLocation() + { + for (var i = 0; i < 9; i++) + { + this.Image.Frames.CreateFrame(); + } + + var frame = this.Image.Frames[4]; + this.Image.Frames.MoveFrame(4, 7); + var newIndex = this.Image.Frames.IndexOf(frame); + Assert.Equal(7, newIndex); + } + + [Fact] + public void IndexOf_ReturnsCorrectIndex() + { + for (var i = 0; i < 9; i++) + { + this.Image.Frames.CreateFrame(); + } + + var frame = this.Image.Frames[4]; + var index = this.Image.Frames.IndexOf(frame); + Assert.Equal(4, index); + } + + [Fact] + public void Contains_TrueIfMember() + { + for (var i = 0; i < 9; i++) + { + this.Image.Frames.CreateFrame(); + } + + var frame = this.Image.Frames[4]; + Assert.True(this.Image.Frames.Contains(frame)); + } + + [Fact] + public void Contains_FalseIfNonMember() + { + for (var i = 0; i < 9; i++) + { + this.Image.Frames.CreateFrame(); + } + + var frame = new ImageFrame(Configuration.Default, 10, 10); + Assert.False(this.Image.Frames.Contains(frame)); + } + + /// + /// Integration test for end-to end API validation. + /// + [Theory] + [WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32)] + public void ConstructGif_FromDifferentPixelTypes(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (Image dest = new Image(source.GetConfiguration(), source.Width, source.Height)) + { + // Giphy.gif has 5 frames + + ImportFrameAs(source.Frames, dest.Frames, 0); + ImportFrameAs(source.Frames, dest.Frames, 1); + ImportFrameAs(source.Frames, dest.Frames, 2); + ImportFrameAs(source.Frames, dest.Frames, 3); + ImportFrameAs(source.Frames, dest.Frames, 4); + + // Drop the original empty root frame: + dest.Frames.RemoveFrame(0); + + dest.DebugSave(provider, appendSourceFileOrDescription: false, extension: "gif"); + dest.CompareToOriginal(provider); + + for (int i = 0; i < 5; i++) + { + CompareGifMetadata(source.Frames[i], dest.Frames[i]); + } + } + } + + private static void ImportFrameAs(ImageFrameCollection source, ImageFrameCollection destination, int index) + where TPixel : struct, IPixel + { + using (Image temp = source.CloneFrame(index)) + { + using (Image temp2 = temp.CloneAs()) + { + destination.AddFrame(temp2.Frames.RootFrame); + } + } + } + + private static void CompareGifMetadata(ImageFrame a, ImageFrame b) + { + // TODO: all metadata classes should be equatable! + + GifFrameMetadata aData = a.Metadata.GetFormatMetadata(GifFormat.Instance); + GifFrameMetadata bData = b.Metadata.GetFormatMetadata(GifFormat.Instance); + + Assert.Equal(aData.DisposalMethod, bData.DisposalMethod); + Assert.Equal(aData.FrameDelay, bData.FrameDelay); + Assert.Equal(aData.ColorTableLength, bData.ColorTableLength); + } + } + } +} diff --git a/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.cs b/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.cs new file mode 100644 index 0000000000..a29f45271b --- /dev/null +++ b/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Text; + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Tests +{ + public abstract partial class ImageFrameCollectionTests : IDisposable + { + protected Image Image { get; } + protected ImageFrameCollection Collection { get; } + + public ImageFrameCollectionTests() + { + // Needed to get English exception messages, which are checked in several tests. + System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("en-US"); + + this.Image = new Image(10, 10); + this.Collection = new ImageFrameCollection(this.Image, 10, 10, default(Rgba32)); + } + + public void Dispose() + { + this.Image.Dispose(); + this.Collection.Dispose(); + } + } +} diff --git a/tests/ImageSharp.Tests/Image/ImageFramesCollectionTests.cs b/tests/ImageSharp.Tests/Image/ImageFramesCollectionTests.cs deleted file mode 100644 index 3923970578..0000000000 --- a/tests/ImageSharp.Tests/Image/ImageFramesCollectionTests.cs +++ /dev/null @@ -1,331 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.PixelFormats; -using Xunit; - -namespace SixLabors.ImageSharp.Tests -{ - public class ImageFramesCollectionTests : IDisposable - { - private Image image; - private ImageFrameCollection collection; - - public ImageFramesCollectionTests() - { - // Needed to get English exception messages, which are checked in several tests. - System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("en-US"); - - this.image = new Image(10, 10); - this.collection = new ImageFrameCollection(this.image, 10, 10, default(Rgba32)); - } - - [Fact] - public void ImageFramesaLwaysHaveOneFrame() - { - Assert.Equal(1, this.collection.Count); - } - - [Fact] - public void AddNewFrame_FramesMustHaveSameSize() - { - ArgumentException ex = Assert.Throws(() => - { - this.collection.AddFrame(new ImageFrame(Configuration.Default, 1, 1)); - }); - - Assert.StartsWith("Frame must have the same dimensions as the image.", ex.Message); - } - - [Fact] - public void AddNewFrame_Frame_FramesNotBeNull() - { - ArgumentNullException ex = Assert.Throws(() => - { - this.collection.AddFrame((ImageFrame)null); - }); - - Assert.StartsWith("Value cannot be null.", ex.Message); - } - - [Fact] - public void AddNewFrame_PixelBuffer_DataMustNotBeNull() - { - Rgba32[] data = null; - - ArgumentNullException ex = Assert.Throws(() => - { - this.collection.AddFrame(data); - }); - - Assert.StartsWith("Value cannot be null.", ex.Message); - } - - [Fact] - public void AddNewFrame_PixelBuffer_BufferIncorrectSize() - { - ArgumentOutOfRangeException ex = Assert.Throws(() => - { - this.collection.AddFrame(new Rgba32[0]); - }); - - Assert.StartsWith("Value 0 must be greater than or equal to 100.", ex.Message); - } - - [Fact] - public void InsertNewFrame_FramesMustHaveSameSize() - { - ArgumentException ex = Assert.Throws(() => - { - this.collection.InsertFrame(1, new ImageFrame(Configuration.Default, 1, 1)); - }); - - Assert.StartsWith("Frame must have the same dimensions as the image.", ex.Message); - } - - [Fact] - public void InsertNewFrame_FramesNotBeNull() - { - ArgumentNullException ex = Assert.Throws(() => - { - this.collection.InsertFrame(1, null); - }); - - Assert.StartsWith("Value cannot be null.", ex.Message); - } - - [Fact] - public void Constructor_FramesMustHaveSameSize() - { - ArgumentException ex = Assert.Throws(() => - { - var collection = new ImageFrameCollection(this.image, new[] { - new ImageFrame(Configuration.Default,10,10), - new ImageFrame(Configuration.Default,1,1) - }); - }); - - Assert.StartsWith("Frame must have the same dimensions as the image.", ex.Message); - } - - [Fact] - public void RemoveAtFrame_ThrowIfRemovingLastFrame() - { - var collection = new ImageFrameCollection(this.image, new[] { - new ImageFrame(Configuration.Default,10,10) - }); - - InvalidOperationException ex = Assert.Throws(() => - { - collection.RemoveFrame(0); - }); - Assert.Equal("Cannot remove last frame.", ex.Message); - } - - [Fact] - public void RemoveAtFrame_CanRemoveFrameZeroIfMultipleFramesExist() - { - - var collection = new ImageFrameCollection(this.image, new[] { - new ImageFrame(Configuration.Default,10,10), - new ImageFrame(Configuration.Default,10,10) - }); - - collection.RemoveFrame(0); - Assert.Equal(1, collection.Count); - } - - [Fact] - public void RootFrameIsFrameAtIndexZero() - { - var collection = new ImageFrameCollection(this.image, new[] { - new ImageFrame(Configuration.Default,10,10), - new ImageFrame(Configuration.Default,10,10) - }); - - Assert.Equal(collection.RootFrame, collection[0]); - } - - [Fact] - public void ConstructorPopulatesFrames() - { - var collection = new ImageFrameCollection(this.image, new[] { - new ImageFrame(Configuration.Default,10,10), - new ImageFrame(Configuration.Default,10,10) - }); - - Assert.Equal(2, collection.Count); - } - - [Fact] - public void DisposeClearsCollection() - { - var collection = new ImageFrameCollection(this.image, new[] { - new ImageFrame(Configuration.Default,10,10), - new ImageFrame(Configuration.Default,10,10) - }); - - collection.Dispose(); - - Assert.Equal(0, collection.Count); - } - - [Fact] - public void Dispose_DisposesAllInnerFrames() - { - var collection = new ImageFrameCollection(this.image, new[] { - new ImageFrame(Configuration.Default,10,10), - new ImageFrame(Configuration.Default,10,10) - }); - - IPixelSource[] framesSnapShot = collection.OfType>().ToArray(); - collection.Dispose(); - - Assert.All(framesSnapShot, f => - { - // the pixel source of the frame is null after its been disposed. - Assert.Null(f.PixelBuffer); - }); - } - - [Theory] - [WithTestPatternImages(10, 10, PixelTypes.Rgba32)] - public void CloneFrame(TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image img = provider.GetImage()) - { - img.Frames.AddFrame(new ImageFrame(Configuration.Default, 10, 10));// add a frame anyway - using (Image cloned = img.Frames.CloneFrame(0)) - { - Assert.Equal(2, img.Frames.Count); - cloned.ComparePixelBufferTo(img.GetPixelSpan()); - } - } - } - - [Theory] - [WithTestPatternImages(10, 10, PixelTypes.Rgba32)] - public void ExtractFrame(TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image img = provider.GetImage()) - { - var sourcePixelData = img.GetPixelSpan().ToArray(); - - img.Frames.AddFrame(new ImageFrame(Configuration.Default, 10, 10)); - using (Image cloned = img.Frames.ExportFrame(0)) - { - Assert.Equal(1, img.Frames.Count); - cloned.ComparePixelBufferTo(sourcePixelData); - } - } - } - - [Fact] - public void CreateFrame_Default() - { - this.image.Frames.CreateFrame(); - - Assert.Equal(2, this.image.Frames.Count); - this.image.Frames[1].ComparePixelBufferTo(default(Rgba32)); - } - - [Fact] - public void CreateFrame_CustomFillColor() - { - this.image.Frames.CreateFrame(Rgba32.HotPink); - - Assert.Equal(2, this.image.Frames.Count); - this.image.Frames[1].ComparePixelBufferTo(Rgba32.HotPink); - } - - [Fact] - public void AddFrameFromPixelData() - { - var pixelData = this.image.Frames.RootFrame.GetPixelSpan().ToArray(); - this.image.Frames.AddFrame(pixelData); - Assert.Equal(2, this.image.Frames.Count); - } - - [Fact] - public void AddFrame_clones_sourceFrame() - { - var pixelData = this.image.Frames.RootFrame.GetPixelSpan().ToArray(); - var otherFRame = new ImageFrame(Configuration.Default, 10, 10); - var addedFrame = this.image.Frames.AddFrame(otherFRame); - addedFrame.ComparePixelBufferTo(otherFRame.GetPixelSpan()); - Assert.NotEqual(otherFRame, addedFrame); - } - - [Fact] - public void InsertFrame_clones_sourceFrame() - { - var pixelData = this.image.Frames.RootFrame.GetPixelSpan().ToArray(); - var otherFRame = new ImageFrame(Configuration.Default, 10, 10); - var addedFrame = this.image.Frames.InsertFrame(0, otherFRame); - addedFrame.ComparePixelBufferTo(otherFRame.GetPixelSpan()); - Assert.NotEqual(otherFRame, addedFrame); - } - - [Fact] - public void MoveFrame_LeavesFrmaeInCorrectLocation() - { - for (var i = 0; i < 9; i++) - { - this.image.Frames.CreateFrame(); - } - - var frame = this.image.Frames[4]; - this.image.Frames.MoveFrame(4, 7); - var newIndex = this.image.Frames.IndexOf(frame); - Assert.Equal(7, newIndex); - } - - - [Fact] - public void IndexOf_ReturnsCorrectIndex() - { - for (var i = 0; i < 9; i++) - { - this.image.Frames.CreateFrame(); - } - - var frame = this.image.Frames[4]; - var index = this.image.Frames.IndexOf(frame); - Assert.Equal(4, index); - } - - [Fact] - public void Contains_TrueIfMember() - { - for (var i = 0; i < 9; i++) - { - this.image.Frames.CreateFrame(); - } - - var frame = this.image.Frames[4]; - Assert.True(this.image.Frames.Contains(frame)); - } - - [Fact] - public void Contains_TFalseIfNoneMember() - { - for (var i = 0; i < 9; i++) - { - this.image.Frames.CreateFrame(); - } - - var frame = new ImageFrame(Configuration.Default, 10, 10); - Assert.False(this.image.Frames.Contains(frame)); - } - - public void Dispose() - { - this.image.Dispose(); - this.collection.Dispose(); - } - } -} diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index 406719ab4f..4f92f4007d 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -2,6 +2,13 @@ + netcoreapp2.1;net462;net472 + True + latest + full + portable + True + SixLabors.ImageSharp.Tests SixLabors.ImageSharp.Tests AnyCPU;x64;x86 SixLabors.ImageSharp.Tests diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.ReferenceImplementations.cs b/tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.ReferenceImplementations.cs new file mode 100644 index 0000000000..d03cfeee22 --- /dev/null +++ b/tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.ReferenceImplementations.cs @@ -0,0 +1,115 @@ +// // Copyright (c) Six Labors and contributors. +// // Licensed under the Apache License, Version 2.0. + +// // Copyright (c) Six Labors and contributors. +// // Licensed under the Apache License, Version 2.0. + +// // Copyright (c) Six Labors and contributors. +// // Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Tests.PixelFormats +{ + public abstract partial class PixelConverterTests + { + public static class ReferenceImplementations + { + public static Rgba32 MakeRgba32(byte r, byte g, byte b, byte a) + { + Rgba32 d = default; + d.R = r; + d.G = g; + d.B = b; + d.A = a; + return d; + } + + public static Argb32 MakeArgb32(byte r, byte g, byte b, byte a) + { + Argb32 d = default; + d.R = r; + d.G = g; + d.B = b; + d.A = a; + return d; + } + + public static Bgra32 MakeBgra32(byte r, byte g, byte b, byte a) + { + Bgra32 d = default; + d.R = r; + d.G = g; + d.B = b; + d.A = a; + return d; + } + + internal static void To( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + where TSourcePixel : struct, IPixel where TDestinationPixel : struct, IPixel + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + + int count = sourcePixels.Length; + ref TSourcePixel sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + + if (typeof(TSourcePixel) == typeof(TDestinationPixel)) + { + Span uniformDest = + MemoryMarshal.Cast(destinationPixels); + sourcePixels.CopyTo(uniformDest); + return; + } + + // Gray8 and Gray16 are special implementations of IPixel in that they do not conform to the + // standard RGBA colorspace format and must be converted from RGBA using the special ITU BT709 algorithm. + // One of the requirements of FromScaledVector4/ToScaledVector4 is that it unaware of this and + // packs/unpacks the pixel without and conversion so we employ custom methods do do this. + if (typeof(TDestinationPixel) == typeof(Gray16)) + { + ref Gray16 gray16Ref = ref MemoryMarshal.GetReference( + MemoryMarshal.Cast(destinationPixels)); + for (int i = 0; i < count; i++) + { + ref TSourcePixel sp = ref Unsafe.Add(ref sourceRef, i); + ref Gray16 dp = ref Unsafe.Add(ref gray16Ref, i); + dp.ConvertFromRgbaScaledVector4(sp.ToScaledVector4()); + } + + return; + } + + if (typeof(TDestinationPixel) == typeof(Gray8)) + { + ref Gray8 gray8Ref = ref MemoryMarshal.GetReference( + MemoryMarshal.Cast(destinationPixels)); + for (int i = 0; i < count; i++) + { + ref TSourcePixel sp = ref Unsafe.Add(ref sourceRef, i); + ref Gray8 dp = ref Unsafe.Add(ref gray8Ref, i); + dp.ConvertFromRgbaScaledVector4(sp.ToScaledVector4()); + } + + return; + } + + // Normal conversion + ref TDestinationPixel destRef = ref MemoryMarshal.GetReference(destinationPixels); + for (int i = 0; i < count; i++) + { + ref TSourcePixel sp = ref Unsafe.Add(ref sourceRef, i); + ref TDestinationPixel dp = ref Unsafe.Add(ref destRef, i); + dp.FromScaledVector4(sp.ToScaledVector4()); + } + } + } + } +} diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.cs index c539e9dcf0..f1f56fcd46 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.cs @@ -5,7 +5,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.PixelFormats { - public abstract class PixelConverterTests + public abstract partial class PixelConverterTests { public static readonly TheoryData RgbaData = new TheoryData @@ -122,39 +122,5 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(expectedPacked, actualPacked); } } - - - private static class ReferenceImplementations - { - public static Rgba32 MakeRgba32(byte r, byte g, byte b, byte a) - { - Rgba32 d = default; - d.R = r; - d.G = g; - d.B = b; - d.A = a; - return d; - } - - public static Argb32 MakeArgb32(byte r, byte g, byte b, byte a) - { - Argb32 d = default; - d.R = r; - d.G = g; - d.B = b; - d.A = a; - return d; - } - - public static Bgra32 MakeBgra32(byte r, byte g, byte b, byte a) - { - Bgra32 d = default; - d.R = r; - d.G = g; - d.B = b; - d.A = a; - return d; - } - } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Bgra5551OperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Bgra5551OperationsTests.cs new file mode 100644 index 0000000000..aa9b7bb1b8 --- /dev/null +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Bgra5551OperationsTests.cs @@ -0,0 +1,20 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +using Xunit.Abstractions; + +namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations +{ + public partial class PixelOperationsTests + { + public class Bgra5551OperationsTests : PixelOperationsTests + { + public Bgra5551OperationsTests(ITestOutputHelper output) + : base(output) + { + } + } + } +} diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Gray16OperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Gray16OperationsTests.cs index c3de335470..f44401ae90 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Gray16OperationsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Gray16OperationsTests.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -25,90 +26,6 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations [Fact] public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); - [Theory] - [MemberData(nameof(ArraySizesData))] - public void FromGray8Bytes(int count) - { - byte[] source = CreateByteTestData(count); - var expected = new Gray16[count]; - - for (int i = 0; i < count; i++) - { - expected[i].FromGray8(new Gray8(source[i])); - } - - TestOperation( - source, - expected, - (s, d) => Operations.FromGray8Bytes(this.Configuration, s, d.GetSpan(), count) - ); - } - - [Theory] - [MemberData(nameof(ArraySizesData))] - public void ToGray8Bytes(int count) - { - Gray16[] source = CreatePixelTestData(count); - byte[] expected = new byte[count]; - var gray = default(Gray8); - - for (int i = 0; i < count; i++) - { - gray.FromScaledVector4(source[i].ToScaledVector4()); - expected[i] = gray.PackedValue; - } - - TestOperation( - source, - expected, - (s, d) => Operations.ToGray8Bytes(this.Configuration, s, d.GetSpan(), count) - ); - } - - [Theory] - [MemberData(nameof(ArraySizesData))] - public void FromGray16Bytes(int count) - { - byte[] source = CreateByteTestData(count * 2); - Span sourceSpan = source.AsSpan(); - var expected = new Gray16[count]; - - for (int i = 0; i < count; i++) - { - int i2 = i * 2; - expected[i].FromGray16(MemoryMarshal.Cast(sourceSpan.Slice(i2, 2))[0]); - } - - TestOperation( - source, - expected, - (s, d) => Operations.FromGray16Bytes(this.Configuration, s, d.GetSpan(), count) - ); - } - - [Theory] - [MemberData(nameof(ArraySizesData))] - public void ToGray16Bytes(int count) - { - Gray16[] source = CreatePixelTestData(count); - byte[] expected = new byte[count * 2]; - Gray16 gray = default; - - for (int i = 0; i < count; i++) - { - int i2 = i * 2; - gray.FromScaledVector4(source[i].ToScaledVector4()); - OctetBytes bytes = Unsafe.As(ref gray); - expected[i2] = bytes[0]; - expected[i2 + 1] = bytes[1]; - } - - TestOperation( - source, - expected, - (s, d) => Operations.ToGray16Bytes(this.Configuration, s, d.GetSpan(), count) - ); - } } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Gray8OperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Gray8OperationsTests.cs index acd6ef23ac..30c4290006 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Gray8OperationsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Gray8OperationsTests.cs @@ -24,91 +24,6 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations [Fact] public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); - - [Theory] - [MemberData(nameof(ArraySizesData))] - public void FromGray8Bytes(int count) - { - byte[] source = CreateByteTestData(count); - var expected = new Gray8[count]; - - for (int i = 0; i < count; i++) - { - expected[i].FromGray8(new Gray8(source[i])); - } - - TestOperation( - source, - expected, - (s, d) => Operations.FromGray8Bytes(this.Configuration, s, d.GetSpan(), count) - ); - } - - [Theory] - [MemberData(nameof(ArraySizesData))] - public void ToGray8Bytes(int count) - { - Gray8[] source = CreatePixelTestData(count); - byte[] expected = new byte[count]; - var gray = default(Gray8); - - for (int i = 0; i < count; i++) - { - gray.FromScaledVector4(source[i].ToScaledVector4()); - expected[i] = gray.PackedValue; - } - - TestOperation( - source, - expected, - (s, d) => Operations.ToGray8Bytes(this.Configuration, s, d.GetSpan(), count) - ); - } - - [Theory] - [MemberData(nameof(ArraySizesData))] - public void FromGray16Bytes(int count) - { - byte[] source = CreateByteTestData(count * 2); - Span sourceSpan = source.AsSpan(); - var expected = new Gray8[count]; - - for (int i = 0; i < count; i++) - { - int i2 = i * 2; - expected[i].FromGray16(MemoryMarshal.Cast(sourceSpan.Slice(i2, 2))[0]); - } - - TestOperation( - source, - expected, - (s, d) => Operations.FromGray16Bytes(this.Configuration, s, d.GetSpan(), count) - ); - } - - [Theory] - [MemberData(nameof(ArraySizesData))] - public void ToGray16Bytes(int count) - { - Gray8[] source = CreatePixelTestData(count); - byte[] expected = new byte[count * 2]; - Gray16 gray = default; - - for (int i = 0; i < count; i++) - { - int i2 = i * 2; - gray.FromScaledVector4(source[i].ToScaledVector4()); - OctetBytes bytes = Unsafe.As(ref gray); - expected[i2] = bytes[0]; - expected[i2 + 1] = bytes[1]; - } - - TestOperation( - source, - expected, - (s, d) => Operations.ToGray16Bytes(this.Configuration, s, d.GetSpan(), count) - ); - } } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Rgb24OperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Rgb24OperationsTests.cs index 1c7d312d30..37793911a2 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Rgb24OperationsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Rgb24OperationsTests.cs @@ -21,4 +21,4 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs index d9ae9131ff..71adc17494 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs @@ -3,12 +3,14 @@ using System; using System.Buffers; +using System.Linq; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.ColorSpaces.Companding; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; using Xunit; using Xunit.Abstractions; @@ -245,7 +247,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations { Vector4Utils.UnPremultiply(ref v); } - + SRgbCompanding.Compress(ref v); } @@ -277,6 +279,33 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations ); } + + public static readonly TheoryData Generic_To_Data = new TheoryData + { + default(Rgba32), + default(Bgra32), + default(Rgb24), + default(Gray8), + default(Gray16), + default(Rgb48), + default(Rgba64) + }; + + [Theory] + [MemberData(nameof(Generic_To_Data))] + public void Generic_To(TDestPixel dummy) + where TDestPixel : struct, IPixel + { + const int Count = 2134; + TPixel[] source = CreatePixelTestData(Count); + TDestPixel[] expected = new TDestPixel[Count]; + + PixelConverterTests.ReferenceImplementations.To(this.Configuration, source, expected); + + TestOperation(source, expected, (s, d) => Operations.To(this.Configuration, (ReadOnlySpan)s, d.GetSpan())); + } + + [Theory] [MemberData(nameof(ArraySizesData))] public void ToScaledVector4(int count) @@ -732,6 +761,92 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations ); } + + [Theory] + [MemberData(nameof(ArraySizesData))] + public void FromGray8(int count) + { + byte[] sourceBytes = CreateByteTestData(count); + Gray8[] source = sourceBytes.Select(b => new Gray8(b)).ToArray(); + var expected = new TPixel[count]; + + + for (int i = 0; i < count; i++) + { + expected[i].FromGray8(source[i]); + } + + TestOperation( + source, + expected, + (s, d) => Operations.FromGray8(this.Configuration, s, d.GetSpan()) + ); + } + + [Theory] + [MemberData(nameof(ArraySizesData))] + public void ToGray8(int count) + { + TPixel[] source = CreatePixelTestData(count); + Gray8[] expected = new Gray8[count]; + + for (int i = 0; i < count; i++) + { + expected[i].FromScaledVector4(source[i].ToScaledVector4()); + } + + TestOperation( + source, + expected, + (s, d) => Operations.ToGray8(this.Configuration, s, d.GetSpan()) + ); + } + + [Theory] + [MemberData(nameof(ArraySizesData))] + public void FromGray16(int count) + { + Gray16[] source = CreateVector4TestData(count).Select(v => + { + Gray16 g = default; + g.FromVector4(v); + return g; + }).ToArray(); + + var expected = new TPixel[count]; + + for (int i = 0; i < count; i++) + { + int i2 = i * 2; + expected[i].FromGray16(source[i]); + } + + TestOperation( + source, + expected, + (s, d) => Operations.FromGray16(this.Configuration, s, d.GetSpan()) + ); + } + + [Theory] + [MemberData(nameof(ArraySizesData))] + public void ToGray16(int count) + { + TPixel[] source = CreatePixelTestData(count); + Gray16[] expected = new Gray16[count]; + + for (int i = 0; i < count; i++) + { + expected[i].FromScaledVector4(source[i].ToScaledVector4()); + } + + TestOperation( + source, + expected, + (s, d) => Operations.ToGray16(this.Configuration, s, d.GetSpan()) + ); + } + public delegate void RefAction(ref T1 arg1); internal static Vector4[] CreateExpectedVector4Data(TPixel[] source, RefAction vectorModifier = null) @@ -903,6 +1018,18 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations // ReSharper restore PossibleNullReferenceException } } + else if (typeof(TDest) == typeof(Gray16)) + { + // Minor difference is tolerated for 16 bit pixel values + Span expected = MemoryMarshal.Cast(this.ExpectedDestBuffer.AsSpan()); + Span actual = MemoryMarshal.Cast(this.ActualDestBuffer.GetSpan()); + + for (int i = 0; i < count; i++) + { + int difference = expected[i].PackedValue - actual[i].PackedValue; + Assert.True(Math.Abs(difference) < 2); + } + } else { Span expected = this.ExpectedDestBuffer.AsSpan(); @@ -915,4 +1042,4 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations } } } -} \ No newline at end of file +}