From 0ef2c3881daff0394330df5ccbe38fb53a46383e Mon Sep 17 00:00:00 2001 From: Simanto Rahman Date: Fri, 5 Oct 2018 00:41:07 -0230 Subject: [PATCH 01/58] Addition of Breadley AdaptiveThreshold --- .../Processing/AdaptiveThresholdExtensions.cs | 47 +++++++ .../AdaptiveThresholdProcessor.cs | 115 ++++++++++++++++++ 2 files changed, 162 insertions(+) create mode 100644 src/ImageSharp/Processing/AdaptiveThresholdExtensions.cs create mode 100644 src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs diff --git a/src/ImageSharp/Processing/AdaptiveThresholdExtensions.cs b/src/ImageSharp/Processing/AdaptiveThresholdExtensions.cs new file mode 100644 index 000000000..9a6d63342 --- /dev/null +++ b/src/ImageSharp/Processing/AdaptiveThresholdExtensions.cs @@ -0,0 +1,47 @@ +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing +{ + /// + /// Extensions to perform AdaptiveThreshold through Mutator + /// + public static class AdaptiveThresholdExtensions + { + /// + /// Applies Bradley Adaptive Threshold to the image. + /// + /// The image this method extends. + /// The pixel format. + /// The . + public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source) + where TPixel : struct, IPixel + => source.ApplyProcessor(new AdaptiveThresholdProcessor()); + + /// + /// Applies Bradley Adaptive Threshold to the image. + /// + /// The image this method extends. + /// Upper (white) color for thresholding. + /// Lower (black) color for thresholding + /// /// The pixel format. + /// The . + public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, TPixel upper, TPixel lower) + where TPixel : struct, IPixel + => source.ApplyProcessor(new AdaptiveThresholdProcessor(upper, lower)); + + /// + /// Applies Bradley Adaptive Threshold to the image. + /// + /// The image this method extends. + /// Upper (white) color for thresholding. + /// Lower (black) color for thresholding + /// Rectangle region to apply the processor on. + /// The pixel format. + /// The . + public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, TPixel upper, TPixel lower, Rectangle rectangle) + where TPixel : struct, IPixel + => source.ApplyProcessor(new AdaptiveThresholdProcessor(upper, lower), rectangle); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs new file mode 100644 index 000000000..b14de6679 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs @@ -0,0 +1,115 @@ +using System; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// Performs Bradley Adaptive Threshold filter against an image + /// + /// The pixel format of the image + internal class AdaptiveThresholdProcessor : IImageProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + public AdaptiveThresholdProcessor() + : this(NamedColors.White, NamedColors.Black) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Color for upper threshold + /// Color for lower threshold + public AdaptiveThresholdProcessor(TPixel upper, TPixel lower) + { + this.Upper = upper; + this.Lower = lower; + } + + /// + /// Gets or sets upper color limit for thresholding + /// + public TPixel Upper { get; set; } + + /// + /// Gets or sets lower color limit for threshold + /// + public TPixel Lower { get; set; } + + public unsafe void Apply(Image source, Rectangle sourceRectangle) + { + ushort xStart = (ushort)Math.Max(0, sourceRectangle.X); + ushort yStart = (ushort)Math.Max(0, sourceRectangle.Y); + ushort xEnd = (ushort)Math.Min(xStart + sourceRectangle.Width, source.Width); + ushort yEnd = (ushort)Math.Min(yStart + sourceRectangle.Height, source.Height); + + // Algorithm variables + uint sum, count; + ushort s = (ushort)Math.Truncate((xEnd / 16f) - 1); + uint[,] intImage = new uint[yEnd, xEnd]; + + // Trying to figure out how to do this + // Using (Buffer2D intImg = source.GetConfiguration().MemoryAllocator.Allocate2D) + Rgb24 rgb = default; + + for (ushort i = yStart; i < yEnd; i++) + { + Span span = source.GetPixelRowSpan(i); + + sum = 0; + + for (ushort j = xStart; j < xEnd; j++) + { + span[j].ToRgb24(ref rgb); + + sum += (uint)(rgb.R + rgb.G + rgb.B); + + if (i != 0) + { + intImage[i, j] = intImage[i - 1, j] + sum; + } + else + { + intImage[i, j] = sum; + } + } + } + + // How can I parallelize this? + ushort x1, x2, y1, y2; + + for (ushort i = yStart; i < yEnd; i++) + { + Span span = source.GetPixelRowSpan(i); + + for (ushort j = xStart; j < xEnd; j++) + { + x1 = (ushort)Math.Max(i - s + 1, 0); + x2 = (ushort)Math.Min(i + s + 1, yEnd - 1); + y1 = (ushort)Math.Max(j - s + 1, 0); + y2 = (ushort)Math.Min(j + s + 1, xEnd - 1); + + count = (ushort)((x2 - x1) * (y2 - y1)); + + sum = intImage[x2, y2] - intImage[x1, y2] - intImage[x2, y1] + intImage[x1, y1]; + + span[j].ToRgb24(ref rgb); + + if ((rgb.R + rgb.G + rgb.B) * count < sum * (1.0 - 0.15)) + { + span[j] = this.Lower; + } + else + { + span[j] = this.Upper; + } + } + } + } + } +} \ No newline at end of file From 5a080aab4cbbb079f8d48c9c4af456a0079ad1df Mon Sep 17 00:00:00 2001 From: Simanto Rahman Date: Fri, 5 Oct 2018 12:35:15 -0230 Subject: [PATCH 02/58] # Added Rect.Intersect # Inherited from ImageProcessor # Minor changes to variables # Minor Tweaks --- .../AdaptiveThresholdProcessor.cs | 107 ++++++++++-------- 1 file changed, 59 insertions(+), 48 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs index b14de6679..46e2e67c0 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs @@ -1,6 +1,8 @@ using System; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Memory; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors @@ -9,7 +11,7 @@ namespace SixLabors.ImageSharp.Processing.Processors /// Performs Bradley Adaptive Threshold filter against an image /// /// The pixel format of the image - internal class AdaptiveThresholdProcessor : IImageProcessor + internal class AdaptiveThresholdProcessor : ImageProcessor where TPixel : struct, IPixel { /// @@ -41,72 +43,81 @@ namespace SixLabors.ImageSharp.Processing.Processors /// public TPixel Lower { get; set; } - public unsafe void Apply(Image source, Rectangle sourceRectangle) + /// + protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - ushort xStart = (ushort)Math.Max(0, sourceRectangle.X); - ushort yStart = (ushort)Math.Max(0, sourceRectangle.Y); - ushort xEnd = (ushort)Math.Min(xStart + sourceRectangle.Width, source.Width); - ushort yEnd = (ushort)Math.Min(yStart + sourceRectangle.Height, source.Height); + var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); + ushort startY = (ushort)interest.Y; + ushort endY = (ushort)interest.Bottom; + ushort startX = (ushort)interest.X; + ushort endX = (ushort)interest.Right; - // Algorithm variables - uint sum, count; - ushort s = (ushort)Math.Truncate((xEnd / 16f) - 1); - uint[,] intImage = new uint[yEnd, xEnd]; + ushort width = (ushort)(endX - startX); + ushort height = (ushort)(endY - startY); - // Trying to figure out how to do this - // Using (Buffer2D intImg = source.GetConfiguration().MemoryAllocator.Allocate2D) - Rgb24 rgb = default; + // Algorithm variables // + ulong sum; + uint count; - for (ushort i = yStart; i < yEnd; i++) - { - Span span = source.GetPixelRowSpan(i); + // Tweaked to support upto 4k wide pixels + ushort s = (ushort)Math.Truncate((width / 16f) - 1); - sum = 0; + // Trying to figure out how to do this + using (Buffer2D intImage = configuration.MemoryAllocator.Allocate2D(width, height, AllocationOptions.Clean)) + { + Rgb24 rgb = default; - for (ushort j = xStart; j < xEnd; j++) + for (ushort i = startY; i < endY; i++) { - span[j].ToRgb24(ref rgb); + Span span = source.GetPixelRowSpan(i); - sum += (uint)(rgb.R + rgb.G + rgb.B); + sum = 0; - if (i != 0) + for (ushort j = startX; j < endX; j++) { - intImage[i, j] = intImage[i - 1, j] + sum; - } - else - { - intImage[i, j] = sum; + span[j].ToRgb24(ref rgb); + + sum += (uint)(rgb.R + rgb.G + rgb.B); + + if (i != 0) + { + intImage[i, j] = intImage[i - 1, j] + sum; + } + else + { + intImage[i, j] = sum; + } } } - } - // How can I parallelize this? - ushort x1, x2, y1, y2; + // How can I parallelize this? + ushort x1, x2, y1, y2; - for (ushort i = yStart; i < yEnd; i++) - { - Span span = source.GetPixelRowSpan(i); - - for (ushort j = xStart; j < xEnd; j++) + for (ushort i = startY; i < endY; i++) { - x1 = (ushort)Math.Max(i - s + 1, 0); - x2 = (ushort)Math.Min(i + s + 1, yEnd - 1); - y1 = (ushort)Math.Max(j - s + 1, 0); - y2 = (ushort)Math.Min(j + s + 1, xEnd - 1); + Span span = source.GetPixelRowSpan(i); - count = (ushort)((x2 - x1) * (y2 - y1)); + for (ushort j = startX; j < endX; j++) + { + x1 = (ushort)Math.Max(i - s + 1, 0); + x2 = (ushort)Math.Min(i + s + 1, endY - 1); + y1 = (ushort)Math.Max(j - s + 1, 0); + y2 = (ushort)Math.Min(j + s + 1, endX - 1); - sum = intImage[x2, y2] - intImage[x1, y2] - intImage[x2, y1] + intImage[x1, y1]; + count = (uint)((x2 - x1) * (y2 - y1)); - span[j].ToRgb24(ref rgb); + sum = intImage[x2, y2] - intImage[x1, y2] - intImage[x2, y1] + intImage[x1, y1]; - if ((rgb.R + rgb.G + rgb.B) * count < sum * (1.0 - 0.15)) - { - span[j] = this.Lower; - } - else - { - span[j] = this.Upper; + span[j].ToRgb24(ref rgb); + + if ((rgb.R + rgb.G + rgb.B) * count < sum * (1.0 - 0.15)) + { + span[j] = this.Lower; + } + else + { + span[j] = this.Upper; + } } } } From 625ea86dd36ea4a79dab51698bd00b7607dc2d00 Mon Sep 17 00:00:00 2001 From: SimantoR Date: Sat, 13 Oct 2018 04:01:48 -0230 Subject: [PATCH 03/58] Added parallelism to loops --- .../AdaptiveThresholdProcessor.cs | 127 +++++++++++------- tests/Images/External | 2 +- 2 files changed, 76 insertions(+), 53 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs index 46e2e67c0..898e1f8fd 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs @@ -1,6 +1,11 @@ -using System; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +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; @@ -46,80 +51,98 @@ namespace SixLabors.ImageSharp.Processing.Processors /// protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); - ushort startY = (ushort)interest.Y; - ushort endY = (ushort)interest.Bottom; - ushort startX = (ushort)interest.X; - ushort endX = (ushort)interest.Right; + var intersect = Rectangle.Intersect(sourceRectangle, source.Bounds()); + ushort startY = (ushort)intersect.Y; + ushort endY = (ushort)intersect.Bottom; + ushort startX = (ushort)intersect.X; + ushort endX = (ushort)intersect.Right; ushort width = (ushort)(endX - startX); ushort height = (ushort)(endY - startY); - // Algorithm variables // - ulong sum; - uint count; - - // Tweaked to support upto 4k wide pixels - ushort s = (ushort)Math.Truncate((width / 16f) - 1); + // Tweaked to support upto 4k wide pixels and not more. 4096 / 16 is 256 thus the '-1' + byte s = (byte)Math.Truncate((width / 16f) - 1); - // Trying to figure out how to do this + // Using pooled 2d buffer for integer image table using (Buffer2D intImage = configuration.MemoryAllocator.Allocate2D(width, height, AllocationOptions.Clean)) { - Rgb24 rgb = default; - - for (ushort i = startY; i < endY; i++) - { - Span span = source.GetPixelRowSpan(i); - - sum = 0; + var workingnRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); - for (ushort j = startX; j < endX; j++) + ParallelHelper.IterateRows( + workingnRectangle, + configuration, + rows => { - span[j].ToRgb24(ref rgb); + ulong sum; - sum += (uint)(rgb.R + rgb.G + rgb.B); + Rgb24 rgb = default; - if (i != 0) + for (int i = rows.Min; i < rows.Max; i++) { - intImage[i, j] = intImage[i - 1, j] + sum; - } - else - { - intImage[i, j] = sum; + var row = source.GetPixelRowSpan(i); + + sum = 0; + + for (int j = startX; j < endX; j++) + { + row[j].ToRgb24(ref rgb); + sum += (ulong)(rgb.B + rgb.G + rgb.B); + + if (i != 0) + { + intImage[i, j] = intImage[i - 1, j] + sum; + } + else + { + intImage[i, j] = sum; + } + } } } - } + ); - // How can I parallelize this? - ushort x1, x2, y1, y2; + ParallelHelper.IterateRows( + workingnRectangle, + configuration, + rows => + { + ushort x1, x2, y1, y2; - for (ushort i = startY; i < endY; i++) - { - Span span = source.GetPixelRowSpan(i); + uint count; - for (ushort j = startX; j < endX; j++) - { - x1 = (ushort)Math.Max(i - s + 1, 0); - x2 = (ushort)Math.Min(i + s + 1, endY - 1); - y1 = (ushort)Math.Max(j - s + 1, 0); - y2 = (ushort)Math.Min(j + s + 1, endX - 1); + long sum; - count = (uint)((x2 - x1) * (y2 - y1)); + for (int i = rows.Min; i < rows.Max; i++) + { + var row = source.GetPixelRowSpan(i); - sum = intImage[x2, y2] - intImage[x1, y2] - intImage[x2, y1] + intImage[x1, y1]; + Rgb24 rgb = default; - span[j].ToRgb24(ref rgb); + for (int j = startX; j < endX; j++) + { + x1 = (ushort)Math.Max(i - s + 1, 0); + x2 = (ushort)Math.Min(i + s + 1, endY - 1); + y1 = (ushort)Math.Max(j - s + 1, 0); + y2 = (ushort)Math.Min(j + s + 1, endX - 1); - if ((rgb.R + rgb.G + rgb.B) * count < sum * (1.0 - 0.15)) - { - span[j] = this.Lower; - } - else - { - span[j] = this.Upper; + count = (uint)((x2 - x1) * (y2 - y1)); + + sum = (long)(intImage[x2, y2] - intImage[x1, y2] - intImage[x2, y1] + intImage[x1, y1]); + + row[j].ToRgb24(ref rgb); + + if ((rgb.R + rgb.G + rgb.B) * count < sum * (1.0 - 0.15)) + { + row[j] = this.Lower; + } + else + { + row[j] = this.Upper; + } + } } } - } + ); } } } diff --git a/tests/Images/External b/tests/Images/External index ee90e5f32..5f3cbd839 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit ee90e5f32218027744b5d40058b587cc1047b76f +Subproject commit 5f3cbd839fbbffae615d294d1dabafdcabc64cf9 From 616c7731b8e9d6c257f819e5f58a1e4a1eb17b3c Mon Sep 17 00:00:00 2001 From: Simanto Rahman Date: Wed, 24 Oct 2018 13:38:07 -0230 Subject: [PATCH 04/58] Temporary fix to accomodate #744 --- .../AdaptiveThresholdProcessor.cs | 85 ++++++++++--------- 1 file changed, 46 insertions(+), 39 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs index 898e1f8fd..baecdf7f0 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Threading.Tasks; +using System.Buffers; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.ParallelUtils; @@ -34,8 +34,8 @@ namespace SixLabors.ImageSharp.Processing.Processors /// Color for lower threshold public AdaptiveThresholdProcessor(TPixel upper, TPixel lower) { - this.Upper = upper; - this.Lower = lower; + this.Upper.PackFromRgba32(upper.ToRgba32()); + this.Lower.PackFromRgba32(lower.ToRgba32()); } /// @@ -75,26 +75,29 @@ namespace SixLabors.ImageSharp.Processing.Processors { ulong sum; - Rgb24 rgb = default; - for (int i = rows.Min; i < rows.Max; i++) { - var row = source.GetPixelRowSpan(i); - - sum = 0; - - for (int j = startX; j < endX; j++) + using (IMemoryOwner tmpPixels = configuration.MemoryAllocator.Allocate(width, AllocationOptions.None)) { - row[j].ToRgb24(ref rgb); - sum += (ulong)(rgb.B + rgb.G + rgb.B); + Span span = tmpPixels.GetSpan(); + PixelOperations.Instance.ToRgb24(source.GetPixelRowSpan(i), span, width); - if (i != 0) - { - intImage[i, j] = intImage[i - 1, j] + sum; - } - else + sum = 0; + + for (int j = startX; j < endX; j++) { - intImage[i, j] = sum; + ref Rgb24 rgb = ref span[(width * j) + i]; + + sum += (ulong)(rgb.B + rgb.G + rgb.B); + + if (i != 0) + { + intImage[i, j] = intImage[i - 1, j] + sum; + } + else + { + intImage[i, j] = sum; + } } } } @@ -114,30 +117,34 @@ namespace SixLabors.ImageSharp.Processing.Processors for (int i = rows.Min; i < rows.Max; i++) { - var row = source.GetPixelRowSpan(i); - - Rgb24 rgb = default; - - for (int j = startX; j < endX; j++) + using (IMemoryOwner tmpPixes = configuration.MemoryAllocator.Allocate(width)) { - x1 = (ushort)Math.Max(i - s + 1, 0); - x2 = (ushort)Math.Min(i + s + 1, endY - 1); - y1 = (ushort)Math.Max(j - s + 1, 0); - y2 = (ushort)Math.Min(j + s + 1, endX - 1); - - count = (uint)((x2 - x1) * (y2 - y1)); - - sum = (long)(intImage[x2, y2] - intImage[x1, y2] - intImage[x2, y1] + intImage[x1, y1]); + Span span = tmpPixes.GetSpan(); + PixelOperations.Instance.ToRgb24(source.GetPixelRowSpan(i), span, width); - row[j].ToRgb24(ref rgb); - - if ((rgb.R + rgb.G + rgb.B) * count < sum * (1.0 - 0.15)) - { - row[j] = this.Lower; - } - else + for (int j = startX; j < endX; j++) { - row[j] = this.Upper; + ref Rgb24 rgb = ref span[(width * j) + 1]; + + x1 = (ushort)Math.Max(i - s + 1, 0); + x2 = (ushort)Math.Min(i + s + 1, endY - 1); + y1 = (ushort)Math.Max(j - s + 1, 0); + y2 = (ushort)Math.Min(j + s + 1, endX - 1); + + count = (uint)((x2 - x1) * (y2 - y1)); + + sum = (long)(intImage[x2, y2] - intImage[x1, y2] - intImage[x2, y1] + intImage[x1, y1]) + + if ((rgb.R + rgb.G + rgb.B) * count < sum * (1.0 - 0.15)) + { + //row[j] = this.Lower; + rgb = this.Lower.ToRgba32().Rgb; + } + else + { + //row[j] = this.Upper; + rgb = this.Upper.ToRgba32().Rgb; + } } } } From c82f0c567ec3de0c471744a36bb1ed904fe073fd Mon Sep 17 00:00:00 2001 From: Simanto Rahman Date: Wed, 24 Oct 2018 13:42:10 -0230 Subject: [PATCH 05/58] Few general changes without effecting the algorithm implementation --- .../AdaptiveThresholdProcessor.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs index baecdf7f0..50dfdfd9e 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs @@ -57,11 +57,13 @@ namespace SixLabors.ImageSharp.Processing.Processors ushort startX = (ushort)intersect.X; ushort endX = (ushort)intersect.Right; - ushort width = (ushort)(endX - startX); - ushort height = (ushort)(endY - startY); + ushort width = (ushort)intersect.Width; + ushort height = (ushort)intersect.Height; // Tweaked to support upto 4k wide pixels and not more. 4096 / 16 is 256 thus the '-1' - byte s = (byte)Math.Truncate((width / 16f) - 1); + byte clusterSize = (byte)((width / 16) - 1); + + float threshold = 0.85f; // Using pooled 2d buffer for integer image table using (Buffer2D intImage = configuration.MemoryAllocator.Allocate2D(width, height, AllocationOptions.Clean)) @@ -126,23 +128,21 @@ namespace SixLabors.ImageSharp.Processing.Processors { ref Rgb24 rgb = ref span[(width * j) + 1]; - x1 = (ushort)Math.Max(i - s + 1, 0); - x2 = (ushort)Math.Min(i + s + 1, endY - 1); - y1 = (ushort)Math.Max(j - s + 1, 0); - y2 = (ushort)Math.Min(j + s + 1, endX - 1); + x1 = (ushort)Math.Max(i - clusterSize + 1, 0); + x2 = (ushort)Math.Min(i + clusterSize + 1, endY - 1); + y1 = (ushort)Math.Max(j - clusterSize + 1, 0); + y2 = (ushort)Math.Min(j + clusterSize + 1, endX - 1); count = (uint)((x2 - x1) * (y2 - y1)); sum = (long)(intImage[x2, y2] - intImage[x1, y2] - intImage[x2, y1] + intImage[x1, y1]) - if ((rgb.R + rgb.G + rgb.B) * count < sum * (1.0 - 0.15)) + if ((rgb.R + rgb.G + rgb.B) * count < sum * threshold) { - //row[j] = this.Lower; rgb = this.Lower.ToRgba32().Rgb; } else { - //row[j] = this.Upper; rgb = this.Upper.ToRgba32().Rgb; } } From cecfcbaaf3166883da9496c1e3a6c41cf2876d38 Mon Sep 17 00:00:00 2001 From: Simanto Rahman Date: Wed, 24 Oct 2018 13:57:29 -0230 Subject: [PATCH 06/58] Fixed few breaking changes --- .../AdaptiveThresholdProcessor.cs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs index 50dfdfd9e..d13b84b8a 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs @@ -19,6 +19,8 @@ namespace SixLabors.ImageSharp.Processing.Processors internal class AdaptiveThresholdProcessor : ImageProcessor where TPixel : struct, IPixel { + private readonly PixelOperations pixelOpInstance; + /// /// Initializes a new instance of the class. /// @@ -34,19 +36,20 @@ namespace SixLabors.ImageSharp.Processing.Processors /// Color for lower threshold public AdaptiveThresholdProcessor(TPixel upper, TPixel lower) { - this.Upper.PackFromRgba32(upper.ToRgba32()); - this.Lower.PackFromRgba32(lower.ToRgba32()); + this.pixelOpInstance = PixelOperations.Instance; + this.Upper.FromRgba32(upper.ToRgba32()); + this.Lower.FromRgba32(lower.ToRgba32()); } /// /// Gets or sets upper color limit for thresholding /// - public TPixel Upper { get; set; } + public Rgb24 Upper { get; set; } /// /// Gets or sets lower color limit for threshold /// - public TPixel Lower { get; set; } + public Rgb24 Lower { get; set; } /// protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) @@ -82,7 +85,7 @@ namespace SixLabors.ImageSharp.Processing.Processors using (IMemoryOwner tmpPixels = configuration.MemoryAllocator.Allocate(width, AllocationOptions.None)) { Span span = tmpPixels.GetSpan(); - PixelOperations.Instance.ToRgb24(source.GetPixelRowSpan(i), span, width); + this.pixelOpInstance.ToRgb24(source.GetPixelRowSpan(i), span); sum = 0; @@ -122,7 +125,7 @@ namespace SixLabors.ImageSharp.Processing.Processors using (IMemoryOwner tmpPixes = configuration.MemoryAllocator.Allocate(width)) { Span span = tmpPixes.GetSpan(); - PixelOperations.Instance.ToRgb24(source.GetPixelRowSpan(i), span, width); + this.pixelOpInstance.ToRgb24(source.GetPixelRowSpan(i), span); for (int j = startX; j < endX; j++) { @@ -139,11 +142,11 @@ namespace SixLabors.ImageSharp.Processing.Processors if ((rgb.R + rgb.G + rgb.B) * count < sum * threshold) { - rgb = this.Lower.ToRgba32().Rgb; + rgb = this.Lower; } else { - rgb = this.Upper.ToRgba32().Rgb; + rgb = this.Upper; } } } From 7f457f5d4573e0aa5ebd39bf6a6b1763a47353aa Mon Sep 17 00:00:00 2001 From: Simanto Rahman Date: Wed, 24 Oct 2018 14:14:20 -0230 Subject: [PATCH 07/58] Missed an end of line by accident :p --- .../AdaptiveThresholdProcessor.cs | 92 +++++++++---------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs index d13b84b8a..b6748ce00 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs @@ -77,81 +77,81 @@ namespace SixLabors.ImageSharp.Processing.Processors workingnRectangle, configuration, rows => - { - ulong sum; - - for (int i = rows.Min; i < rows.Max; i++) { - using (IMemoryOwner tmpPixels = configuration.MemoryAllocator.Allocate(width, AllocationOptions.None)) - { - Span span = tmpPixels.GetSpan(); - this.pixelOpInstance.ToRgb24(source.GetPixelRowSpan(i), span); + ulong sum; - sum = 0; - - for (int j = startX; j < endX; j++) + for (int i = rows.Min; i < rows.Max; i++) + { + using (IMemoryOwner tmpPixels = configuration.MemoryAllocator.Allocate(width, AllocationOptions.None)) { - ref Rgb24 rgb = ref span[(width * j) + i]; + Span span = tmpPixels.GetSpan(); + this.pixelOpInstance.ToRgb24(source.GetPixelRowSpan(i), span); - sum += (ulong)(rgb.B + rgb.G + rgb.B); + sum = 0; - if (i != 0) + for (int j = startX; j < endX; j++) { - intImage[i, j] = intImage[i - 1, j] + sum; - } - else - { - intImage[i, j] = sum; + ref Rgb24 rgb = ref span[(width * j) + i]; + + sum += (ulong)(rgb.B + rgb.G + rgb.B); + + if (i != 0) + { + intImage[i, j] = intImage[i - 1, j] + sum; + } + else + { + intImage[i, j] = sum; + } } } } } - } ); ParallelHelper.IterateRows( workingnRectangle, configuration, rows => - { - ushort x1, x2, y1, y2; + { + ushort x1, x2, y1, y2; - uint count; + uint count; - long sum; + long sum; - for (int i = rows.Min; i < rows.Max; i++) - { - using (IMemoryOwner tmpPixes = configuration.MemoryAllocator.Allocate(width)) + for (int i = rows.Min; i < rows.Max; i++) { - Span span = tmpPixes.GetSpan(); - this.pixelOpInstance.ToRgb24(source.GetPixelRowSpan(i), span); - - for (int j = startX; j < endX; j++) + using (IMemoryOwner tmpPixes = configuration.MemoryAllocator.Allocate(width)) { - ref Rgb24 rgb = ref span[(width * j) + 1]; + Span span = tmpPixes.GetSpan(); + this.pixelOpInstance.ToRgb24(source.GetPixelRowSpan(i), span); - x1 = (ushort)Math.Max(i - clusterSize + 1, 0); - x2 = (ushort)Math.Min(i + clusterSize + 1, endY - 1); - y1 = (ushort)Math.Max(j - clusterSize + 1, 0); - y2 = (ushort)Math.Min(j + clusterSize + 1, endX - 1); + for (int j = startX; j < endX; j++) + { + ref Rgb24 rgb = ref span[(width * j) + 1]; - count = (uint)((x2 - x1) * (y2 - y1)); + x1 = (ushort)Math.Max(i - clusterSize + 1, 0); + x2 = (ushort)Math.Min(i + clusterSize + 1, endY - 1); + y1 = (ushort)Math.Max(j - clusterSize + 1, 0); + y2 = (ushort)Math.Min(j + clusterSize + 1, endX - 1); - sum = (long)(intImage[x2, y2] - intImage[x1, y2] - intImage[x2, y1] + intImage[x1, y1]) + count = (uint)((x2 - x1) * (y2 - y1)); - if ((rgb.R + rgb.G + rgb.B) * count < sum * threshold) - { - rgb = this.Lower; - } - else - { - rgb = this.Upper; + sum = (long)(intImage[x2, y2] - intImage[x1, y2] - intImage[x2, y1] + intImage[x1, y1]); + + if ((rgb.R + rgb.G + rgb.B) * count < sum * threshold) + { + rgb = this.Lower; + } + else + { + rgb = this.Upper; + } } } } } - } ); } } From 865607b3e83a8bec7eacbbe0ac22e8b870bf7af2 Mon Sep 17 00:00:00 2001 From: Simanto Rahman Date: Thu, 25 Oct 2018 12:13:48 -0230 Subject: [PATCH 08/58] Used TempBuffer and fixed few logical errors --- .../AdaptiveThresholdProcessor.cs | 130 +++++++++--------- 1 file changed, 62 insertions(+), 68 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs index b6748ce00..0602777e3 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs @@ -37,19 +37,20 @@ namespace SixLabors.ImageSharp.Processing.Processors public AdaptiveThresholdProcessor(TPixel upper, TPixel lower) { this.pixelOpInstance = PixelOperations.Instance; - this.Upper.FromRgba32(upper.ToRgba32()); - this.Lower.FromRgba32(lower.ToRgba32()); + + this.Upper = upper; + this.Lower = lower; } /// /// Gets or sets upper color limit for thresholding /// - public Rgb24 Upper { get; set; } + public TPixel Upper { get; set; } /// /// Gets or sets lower color limit for threshold /// - public Rgb24 Lower { get; set; } + public TPixel Lower { get; set; } /// protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) @@ -69,90 +70,83 @@ namespace SixLabors.ImageSharp.Processing.Processors float threshold = 0.85f; // Using pooled 2d buffer for integer image table - using (Buffer2D intImage = configuration.MemoryAllocator.Allocate2D(width, height, AllocationOptions.Clean)) + using (Buffer2D intImage = configuration.MemoryAllocator.Allocate2D(width, height)) { - var workingnRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); + var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); - ParallelHelper.IterateRows( - workingnRectangle, + ParallelHelper.IterateRowsWithTempBuffer( + workingRectangle, configuration, - rows => + (rows, memory) => + { + ulong sum = 0; + + Span tmpSpan = memory.Span; + + for (int i = rows.Min; i < rows.Max; i++) { - ulong sum; + this.pixelOpInstance.ToRgb24(source.GetPixelRowSpan(i), tmpSpan); + + sum = 0; - for (int i = rows.Min; i < rows.Max; i++) + for (int j = startX; j < endX; j++) { - using (IMemoryOwner tmpPixels = configuration.MemoryAllocator.Allocate(width, AllocationOptions.None)) + ref Rgb24 rgb = ref tmpSpan[j]; + + sum += (ulong)(rgb.R + rgb.G + rgb.B); + + if (i != 0) + { + intImage[i, j] = intImage[i - 1, j] + sum; + } + else { - Span span = tmpPixels.GetSpan(); - this.pixelOpInstance.ToRgb24(source.GetPixelRowSpan(i), span); - - sum = 0; - - for (int j = startX; j < endX; j++) - { - ref Rgb24 rgb = ref span[(width * j) + i]; - - sum += (ulong)(rgb.B + rgb.G + rgb.B); - - if (i != 0) - { - intImage[i, j] = intImage[i - 1, j] + sum; - } - else - { - intImage[i, j] = sum; - } - } + intImage[i, j] = sum; } } } - ); + }); - ParallelHelper.IterateRows( - workingnRectangle, + ParallelHelper.IterateRowsWithTempBuffer( + workingRectangle, configuration, - rows => - { - ushort x1, x2, y1, y2; + (rows, memory) => + { + ushort x1, x2, y1, y2; + uint count = 0; + long sum = 0; - uint count; + Span tmpSpan = memory.Span; - long sum; + for (int i = rows.Min; i < rows.Max; i++) + { + Span originalSpan = source.GetPixelRowSpan(i); + this.pixelOpInstance.ToRgb24(originalSpan, tmpSpan); - for (int i = rows.Min; i < rows.Max; i++) + for (int j = startX; j < endX; j++) { - using (IMemoryOwner tmpPixes = configuration.MemoryAllocator.Allocate(width)) + ref Rgb24 rgb = ref tmpSpan[j]; + + x1 = (ushort)Math.Max(i - clusterSize + 1, 0); + x2 = (ushort)Math.Min(i + clusterSize + 1, endY - 1); + y1 = (ushort)Math.Max(j - clusterSize + 1, 0); + y2 = (ushort)Math.Min(j + clusterSize + 1, endX - 1); + + count = (uint)((x2 - x1) * (y2 - y1)); + + sum = (long)(intImage[x2, y2] - intImage[x1, y2] - intImage[x2, y1] + intImage[x1, y1]); + + if ((rgb.R + rgb.G + rgb.B) * count < sum * threshold) + { + originalSpan[j] = this.Lower; + } + else { - Span span = tmpPixes.GetSpan(); - this.pixelOpInstance.ToRgb24(source.GetPixelRowSpan(i), span); - - for (int j = startX; j < endX; j++) - { - ref Rgb24 rgb = ref span[(width * j) + 1]; - - x1 = (ushort)Math.Max(i - clusterSize + 1, 0); - x2 = (ushort)Math.Min(i + clusterSize + 1, endY - 1); - y1 = (ushort)Math.Max(j - clusterSize + 1, 0); - y2 = (ushort)Math.Min(j + clusterSize + 1, endX - 1); - - count = (uint)((x2 - x1) * (y2 - y1)); - - sum = (long)(intImage[x2, y2] - intImage[x1, y2] - intImage[x2, y1] + intImage[x1, y1]); - - if ((rgb.R + rgb.G + rgb.B) * count < sum * threshold) - { - rgb = this.Lower; - } - else - { - rgb = this.Upper; - } - } + originalSpan[j] = this.Upper; } } } - ); + }); } } } From 3c6410294025fc88b25f8a99ccbd8b067c22b154 Mon Sep 17 00:00:00 2001 From: Simanto Rahman Date: Thu, 25 Oct 2018 12:50:40 -0230 Subject: [PATCH 09/58] Added contructor to control threshold limit --- .../Processing/AdaptiveThresholdExtensions.cs | 42 ++++++++++++++++++- .../AdaptiveThresholdProcessor.cs | 31 +++++++++++--- 2 files changed, 65 insertions(+), 8 deletions(-) diff --git a/src/ImageSharp/Processing/AdaptiveThresholdExtensions.cs b/src/ImageSharp/Processing/AdaptiveThresholdExtensions.cs index 9a6d63342..6c8795aa5 100644 --- a/src/ImageSharp/Processing/AdaptiveThresholdExtensions.cs +++ b/src/ImageSharp/Processing/AdaptiveThresholdExtensions.cs @@ -1,5 +1,5 @@ using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.ImageSharp.Processing.Processors.Binarization; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing @@ -19,18 +19,42 @@ namespace SixLabors.ImageSharp.Processing where TPixel : struct, IPixel => source.ApplyProcessor(new AdaptiveThresholdProcessor()); + /// + /// Applies Bradley Adaptive Threshold to the image. + /// + /// The image this method extends. + /// Threshold limit (0.0-1.0) to consider for binarization. + /// The pixel format. + /// The . + public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, float threshold) + where TPixel : struct, IPixel + => source.ApplyProcessor(new AdaptiveThresholdProcessor(threshold)); + /// /// Applies Bradley Adaptive Threshold to the image. /// /// The image this method extends. /// Upper (white) color for thresholding. /// Lower (black) color for thresholding - /// /// The pixel format. + /// The pixel format. /// The . public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, TPixel upper, TPixel lower) where TPixel : struct, IPixel => source.ApplyProcessor(new AdaptiveThresholdProcessor(upper, lower)); + /// + /// Applies Bradley Adaptive Threshold to the image. + /// + /// The image this method extends. + /// Upper (white) color for thresholding. + /// Lower (black) color for thresholding + /// Threshold limit (0.0-1.0) to consider for binarization. + /// The pixel format. + /// The . + public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, TPixel upper, TPixel lower, float threshold) + where TPixel : struct, IPixel + => source.ApplyProcessor(new AdaptiveThresholdProcessor(upper, lower, threshold)); + /// /// Applies Bradley Adaptive Threshold to the image. /// @@ -43,5 +67,19 @@ namespace SixLabors.ImageSharp.Processing public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, TPixel upper, TPixel lower, Rectangle rectangle) where TPixel : struct, IPixel => source.ApplyProcessor(new AdaptiveThresholdProcessor(upper, lower), rectangle); + + /// + /// Applies Bradley Adaptive Threshold to the image. + /// + /// The image this method extends. + /// Upper (white) color for thresholding. + /// Lower (black) color for thresholding + /// Threshold limit (0.0-1.0) to consider for binarization. + /// Rectangle region to apply the processor on. + /// The pixel format. + /// The . + public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, TPixel upper, TPixel lower, float threshold, Rectangle rectangle) + where TPixel : struct, IPixel + => source.ApplyProcessor(new AdaptiveThresholdProcessor(upper, lower, threshold), rectangle); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs index 0602777e3..2bed73be0 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs @@ -10,7 +10,7 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.Memory; using SixLabors.Primitives; -namespace SixLabors.ImageSharp.Processing.Processors +namespace SixLabors.ImageSharp.Processing.Processors.Binarization { /// /// Performs Bradley Adaptive Threshold filter against an image @@ -25,7 +25,21 @@ namespace SixLabors.ImageSharp.Processing.Processors /// Initializes a new instance of the class. /// public AdaptiveThresholdProcessor() - : this(NamedColors.White, NamedColors.Black) + : this(NamedColors.White, NamedColors.Black, 0.85f) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Threshold limit + public AdaptiveThresholdProcessor(float threshold) + : this(NamedColors.White, NamedColors.Black, threshold) + { + } + + public AdaptiveThresholdProcessor(TPixel upper, TPixel lower) + : this(upper, lower, 0.85f) { } @@ -34,12 +48,14 @@ namespace SixLabors.ImageSharp.Processing.Processors /// /// Color for upper threshold /// Color for lower threshold - public AdaptiveThresholdProcessor(TPixel upper, TPixel lower) + /// Threshold limit + public AdaptiveThresholdProcessor(TPixel upper, TPixel lower, float threshold) { this.pixelOpInstance = PixelOperations.Instance; this.Upper = upper; this.Lower = lower; + this.Threshold = threshold; } /// @@ -52,6 +68,11 @@ namespace SixLabors.ImageSharp.Processing.Processors /// public TPixel Lower { get; set; } + /// + /// Gets or sets the value for threshold limit + /// + public float Threshold { get; set; } + /// protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { @@ -67,8 +88,6 @@ namespace SixLabors.ImageSharp.Processing.Processors // Tweaked to support upto 4k wide pixels and not more. 4096 / 16 is 256 thus the '-1' byte clusterSize = (byte)((width / 16) - 1); - float threshold = 0.85f; - // Using pooled 2d buffer for integer image table using (Buffer2D intImage = configuration.MemoryAllocator.Allocate2D(width, height)) { @@ -136,7 +155,7 @@ namespace SixLabors.ImageSharp.Processing.Processors sum = (long)(intImage[x2, y2] - intImage[x1, y2] - intImage[x2, y1] + intImage[x1, y1]); - if ((rgb.R + rgb.G + rgb.B) * count < sum * threshold) + if ((rgb.R + rgb.G + rgb.B) * count < sum * this.Threshold) { originalSpan[j] = this.Lower; } From 4b801a414c79d43bbb004e7511461b7ecdfe5e75 Mon Sep 17 00:00:00 2001 From: Simanto Rahman Date: Fri, 26 Oct 2018 19:22:03 -0230 Subject: [PATCH 10/58] Fixed several bugs produced during parallelism implementations --- .../AdaptiveThresholdProcessor.cs | 74 +++++++++---------- 1 file changed, 33 insertions(+), 41 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs index 2bed73be0..21c7f4f57 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs @@ -55,7 +55,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization this.Upper = upper; this.Lower = lower; - this.Threshold = threshold; + this.ThresholdLimit = threshold; } /// @@ -71,7 +71,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization /// /// Gets or sets the value for threshold limit /// - public float Threshold { get; set; } + public float ThresholdLimit { get; set; } /// protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) @@ -90,81 +90,73 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization // Using pooled 2d buffer for integer image table using (Buffer2D intImage = configuration.MemoryAllocator.Allocate2D(width, height)) + using (IMemoryOwner tmpBuffer = configuration.MemoryAllocator.Allocate(width * height)) { - var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); + Rectangle workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); - ParallelHelper.IterateRowsWithTempBuffer( + this.pixelOpInstance.ToRgb24(source.GetPixelSpan(), tmpBuffer.GetSpan()); + + ParallelHelper.IterateRows( workingRectangle, configuration, - (rows, memory) => + rows => { - ulong sum = 0; - - Span tmpSpan = memory.Span; - - for (int i = rows.Min; i < rows.Max; i++) + Span rgbSpan = tmpBuffer.GetSpan(); + uint sum; + for (int x = startX; x < endX; x++) { - this.pixelOpInstance.ToRgb24(source.GetPixelRowSpan(i), tmpSpan); - sum = 0; - - for (int j = startX; j < endX; j++) + for (int y = rows.Min; y < rows.Max; y++) { - ref Rgb24 rgb = ref tmpSpan[j]; - - sum += (ulong)(rgb.R + rgb.G + rgb.B); + ref Rgb24 rgb = ref rgbSpan[(width * y) + x]; + sum += (uint)(rgb.R + rgb.G + rgb.B); - if (i != 0) + if (x > 0) { - intImage[i, j] = intImage[i - 1, j] + sum; + intImage[x - startX, y - startY] = intImage[x - 1 - startX, y - startY] + sum; } else { - intImage[i, j] = sum; + intImage[x - startX, y - startY] = sum; } } } }); - ParallelHelper.IterateRowsWithTempBuffer( + ParallelHelper.IterateRows( workingRectangle, configuration, - (rows, memory) => + rows => { ushort x1, x2, y1, y2; - uint count = 0; + Span rgbSpan = tmpBuffer.GetSpan(); long sum = 0; + uint count = 0; - Span tmpSpan = memory.Span; - - for (int i = rows.Min; i < rows.Max; i++) + for (int x = startX; x < endX; x++) { - Span originalSpan = source.GetPixelRowSpan(i); - this.pixelOpInstance.ToRgb24(originalSpan, tmpSpan); - - for (int j = startX; j < endX; j++) + for (int y = rows.Min; y < rows.Max; y++) { - ref Rgb24 rgb = ref tmpSpan[j]; - - x1 = (ushort)Math.Max(i - clusterSize + 1, 0); - x2 = (ushort)Math.Min(i + clusterSize + 1, endY - 1); - y1 = (ushort)Math.Max(j - clusterSize + 1, 0); - y2 = (ushort)Math.Min(j + clusterSize + 1, endX - 1); + ref Rgb24 rgb = ref rgbSpan[(width * y) + x]; + x1 = (ushort)Math.Max(x - clusterSize + 1 - startX, 0); + x2 = (ushort)Math.Min(x + clusterSize + 1 - startX, endX - startX - 1); + y1 = (ushort)Math.Max(y - clusterSize + 1 - startY, 0); + y2 = (ushort)Math.Min(y + clusterSize + 1 - startY, endY - startY - 1); count = (uint)((x2 - x1) * (y2 - y1)); - sum = (long)(intImage[x2, y2] - intImage[x1, y2] - intImage[x2, y1] + intImage[x1, y1]); + sum = (long)(intImage[x2, y2] - intImage[x2, y1] - intImage[x1, y2] + intImage[x1, y1]); - if ((rgb.R + rgb.G + rgb.B) * count < sum * this.Threshold) + if ((rgb.R + rgb.G + rgb.B) * count <= sum * this.ThresholdLimit) { - originalSpan[j] = this.Lower; + source[x, y] = this.Lower; } else { - originalSpan[j] = this.Upper; + source[x, y] = this.Upper; } } - } + } }); } } From de721e89749f92776d1df091602fc9630d0eed3d Mon Sep 17 00:00:00 2001 From: Simanto Rahman Date: Tue, 30 Oct 2018 13:52:02 -0230 Subject: [PATCH 11/58] Algorithm behaves abnormally when applied with ParallelHelpers --- .../AdaptiveThresholdProcessor.cs | 118 +++++++++--------- 1 file changed, 57 insertions(+), 61 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs index 21c7f4f57..030768e69 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs @@ -76,7 +76,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization /// protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - var intersect = Rectangle.Intersect(sourceRectangle, source.Bounds()); + Rectangle intersect = Rectangle.Intersect(sourceRectangle, source.Bounds()); + + // Used ushort because the values should never exceed max ushort value ushort startY = (ushort)intersect.Y; ushort endY = (ushort)intersect.Bottom; ushort startX = (ushort)intersect.X; @@ -85,79 +87,73 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization ushort width = (ushort)intersect.Width; ushort height = (ushort)intersect.Height; - // Tweaked to support upto 4k wide pixels and not more. 4096 / 16 is 256 thus the '-1' - byte clusterSize = (byte)((width / 16) - 1); + // ClusterSize defines the size of cluster to used to check for average. Tweaked to support upto 4k wide pixels and not more. 4096 / 16 is 256 thus the '-1' + byte clusterSize = (byte)Math.Truncate((width / 16f) - 1); - // Using pooled 2d buffer for integer image table + // Using pooled 2d buffer for integer image table and temp memory to hold Rgb24 converted pixel data using (Buffer2D intImage = configuration.MemoryAllocator.Allocate2D(width, height)) - using (IMemoryOwner tmpBuffer = configuration.MemoryAllocator.Allocate(width * height)) + using (IMemoryOwner bulkRgbBuf = configuration.MemoryAllocator.Allocate(width * height)) { + // Defines the rectangle section of the image to work on Rectangle workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); - this.pixelOpInstance.ToRgb24(source.GetPixelSpan(), tmpBuffer.GetSpan()); + // TPixel span of the original image + Span pixelSpan = source.GetPixelSpan(); + + // RGB24 span of the converted pixel buffer + Span rgbSpan = bulkRgbBuf.GetSpan(); - ParallelHelper.IterateRows( - workingRectangle, - configuration, - rows => + // Bulk conversion to RGB24 + this.pixelOpInstance.ToRgb24(pixelSpan, rgbSpan); + + for (int x = startX; x < endX; x++) + { + ulong sum = 0; + for (int y = startY; y < endY; y++) { - Span rgbSpan = tmpBuffer.GetSpan(); - uint sum; - for (int x = startX; x < endX; x++) + ref Rgb24 rgb = ref rgbSpan[(width * y) + x]; + + sum += (ulong)(rgb.R + rgb.G + rgb.B); + + if (x != 0) + { + intImage[x - startX, y - startY] = intImage[x - startX - 1, y - startY] + sum; + } + else { - sum = 0; - for (int y = rows.Min; y < rows.Max; y++) - { - ref Rgb24 rgb = ref rgbSpan[(width * y) + x]; - sum += (uint)(rgb.R + rgb.G + rgb.B); - - if (x > 0) - { - intImage[x - startX, y - startY] = intImage[x - 1 - startX, y - startY] + sum; - } - else - { - intImage[x - startX, y - startY] = sum; - } - } + intImage[x - startX, y - startY] = sum; } - }); + } + } - ParallelHelper.IterateRows( - workingRectangle, - configuration, - rows => + ushort x1, x2, y1, y2; + uint count = 0; + + for (int x = startX; x < endX; x++) + { + long sum = 0; + for (int y = startY; y < endY; y++) { - ushort x1, x2, y1, y2; - Span rgbSpan = tmpBuffer.GetSpan(); - long sum = 0; - uint count = 0; + ref Rgb24 rgb = ref rgbSpan[(width * y) + x]; + + x1 = (ushort)Math.Max(x - startX - clusterSize + 1, 0); + x2 = (ushort)Math.Min(x - startX + clusterSize + 1, width - 1); + y1 = (ushort)Math.Max(y - startY - clusterSize + 1, 0); + y2 = (ushort)Math.Min(y - startY + clusterSize + 1, height - 1); + + count = (uint)((x2 - x1) * (y2 - y1)); + sum = (long)(intImage[x2, y2] - intImage[x1, y2] - intImage[x2, y1] + intImage[x1, y1]); - for (int x = startX; x < endX; x++) + if ((rgb.R + rgb.G + rgb.B) * count <= sum * this.ThresholdLimit) { - for (int y = rows.Min; y < rows.Max; y++) - { - ref Rgb24 rgb = ref rgbSpan[(width * y) + x]; - x1 = (ushort)Math.Max(x - clusterSize + 1 - startX, 0); - x2 = (ushort)Math.Min(x + clusterSize + 1 - startX, endX - startX - 1); - y1 = (ushort)Math.Max(y - clusterSize + 1 - startY, 0); - y2 = (ushort)Math.Min(y + clusterSize + 1 - startY, endY - startY - 1); - - count = (uint)((x2 - x1) * (y2 - y1)); - - sum = (long)(intImage[x2, y2] - intImage[x2, y1] - intImage[x1, y2] + intImage[x1, y1]); - - if ((rgb.R + rgb.G + rgb.B) * count <= sum * this.ThresholdLimit) - { - source[x, y] = this.Lower; - } - else - { - source[x, y] = this.Upper; - } - } - } - }); + pixelSpan[(width * y) + x] = this.Lower; + } + else + { + pixelSpan[(width * y) + x] = this.Upper; + } + } + } } } } From d89daae13a504db2a699265e803e9bdda26629ac Mon Sep 17 00:00:00 2001 From: Simanto Rahman Date: Tue, 30 Oct 2018 19:01:56 -0230 Subject: [PATCH 12/58] Fully working implementation --- .../AdaptiveThresholdProcessor.cs | 70 +++++++++---------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs index 030768e69..4fb97aa9f 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs @@ -92,29 +92,22 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization // Using pooled 2d buffer for integer image table and temp memory to hold Rgb24 converted pixel data using (Buffer2D intImage = configuration.MemoryAllocator.Allocate2D(width, height)) - using (IMemoryOwner bulkRgbBuf = configuration.MemoryAllocator.Allocate(width * height)) + using (IMemoryOwner tmpBuffer = configuration.MemoryAllocator.Allocate(width * height)) { // Defines the rectangle section of the image to work on Rectangle workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); - // TPixel span of the original image - Span pixelSpan = source.GetPixelSpan(); - - // RGB24 span of the converted pixel buffer - Span rgbSpan = bulkRgbBuf.GetSpan(); - - // Bulk conversion to RGB24 - this.pixelOpInstance.ToRgb24(pixelSpan, rgbSpan); + this.pixelOpInstance.ToRgb24(source.GetPixelSpan(), tmpBuffer.GetSpan()); for (int x = startX; x < endX; x++) { + Span rgbSpan = tmpBuffer.GetSpan(); ulong sum = 0; for (int y = startY; y < endY; y++) { ref Rgb24 rgb = ref rgbSpan[(width * y) + x]; - sum += (ulong)(rgb.R + rgb.G + rgb.B); - + sum += (ulong)(rgb.R + rgb.G + rgb.G); if (x != 0) { intImage[x - startX, y - startY] = intImage[x - startX - 1, y - startY] + sum; @@ -126,34 +119,41 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization } } - ushort x1, x2, y1, y2; - uint count = 0; - - for (int x = startX; x < endX; x++) - { - long sum = 0; - for (int y = startY; y < endY; y++) + ParallelHelper.IterateRows( + workingRectangle, + configuration, + rows => { - ref Rgb24 rgb = ref rgbSpan[(width * y) + x]; + Span rgbSpan = tmpBuffer.GetSpan(); + ushort x1, y1, x2, y2; + uint count = 0; + long sum = 0; - x1 = (ushort)Math.Max(x - startX - clusterSize + 1, 0); - x2 = (ushort)Math.Min(x - startX + clusterSize + 1, width - 1); - y1 = (ushort)Math.Max(y - startY - clusterSize + 1, 0); - y2 = (ushort)Math.Min(y - startY + clusterSize + 1, height - 1); - - count = (uint)((x2 - x1) * (y2 - y1)); - sum = (long)(intImage[x2, y2] - intImage[x1, y2] - intImage[x2, y1] + intImage[x1, y1]); - - if ((rgb.R + rgb.G + rgb.B) * count <= sum * this.ThresholdLimit) + for (int x = startX; x < endX; x++) { - pixelSpan[(width * y) + x] = this.Lower; + for (int y = rows.Min; y < rows.Max; y++) + { + ref Rgb24 rgb = ref rgbSpan[(width * y) + x]; + + x1 = (ushort)Math.Max(x - startX - clusterSize + 1, 0); + x2 = (ushort)Math.Min(x - startX + clusterSize + 1, width - 1); + y1 = (ushort)Math.Max(y - startY - clusterSize + 1, 0); + y2 = (ushort)Math.Min(y - startY + clusterSize + 1, height - 1); + + count = (uint)((x2 - x1) * (y2 - y1)); + sum = (long)(intImage[x2, y2] - intImage[x1, y2] - intImage[x2, y1] + intImage[x1, y1]); + + if ((rgb.R + rgb.G + rgb.B) * count <= sum * this.ThresholdLimit) + { + source[x, y] = this.Lower; + } + else + { + source[x, y] = this.Upper; + } + } } - else - { - pixelSpan[(width * y) + x] = this.Upper; - } - } - } + }); } } } From 2a58efb39f619577f3085850fe16ffa93effab8b Mon Sep 17 00:00:00 2001 From: Simanto Rahman Date: Wed, 31 Oct 2018 12:45:56 -0230 Subject: [PATCH 13/58] Changed naming convention for threshold limit param --- .../Processing/AdaptiveThresholdExtensions.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/ImageSharp/Processing/AdaptiveThresholdExtensions.cs b/src/ImageSharp/Processing/AdaptiveThresholdExtensions.cs index 6c8795aa5..33cf4b45b 100644 --- a/src/ImageSharp/Processing/AdaptiveThresholdExtensions.cs +++ b/src/ImageSharp/Processing/AdaptiveThresholdExtensions.cs @@ -23,12 +23,12 @@ namespace SixLabors.ImageSharp.Processing /// Applies Bradley Adaptive Threshold to the image. /// /// The image this method extends. - /// Threshold limit (0.0-1.0) to consider for binarization. + /// Threshold limit (0.0-1.0) to consider for binarization. /// The pixel format. /// The . - public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, float threshold) + public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, float thresholdLimit) where TPixel : struct, IPixel - => source.ApplyProcessor(new AdaptiveThresholdProcessor(threshold)); + => source.ApplyProcessor(new AdaptiveThresholdProcessor(thresholdLimit)); /// /// Applies Bradley Adaptive Threshold to the image. @@ -48,12 +48,12 @@ namespace SixLabors.ImageSharp.Processing /// The image this method extends. /// Upper (white) color for thresholding. /// Lower (black) color for thresholding - /// Threshold limit (0.0-1.0) to consider for binarization. + /// Threshold limit (0.0-1.0) to consider for binarization. /// The pixel format. /// The . - public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, TPixel upper, TPixel lower, float threshold) + public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, TPixel upper, TPixel lower, float thresholdLimit) where TPixel : struct, IPixel - => source.ApplyProcessor(new AdaptiveThresholdProcessor(upper, lower, threshold)); + => source.ApplyProcessor(new AdaptiveThresholdProcessor(upper, lower, thresholdLimit)); /// /// Applies Bradley Adaptive Threshold to the image. @@ -74,12 +74,12 @@ namespace SixLabors.ImageSharp.Processing /// The image this method extends. /// Upper (white) color for thresholding. /// Lower (black) color for thresholding - /// Threshold limit (0.0-1.0) to consider for binarization. + /// Threshold limit (0.0-1.0) to consider for binarization. /// Rectangle region to apply the processor on. /// The pixel format. /// The . - public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, TPixel upper, TPixel lower, float threshold, Rectangle rectangle) + public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, TPixel upper, TPixel lower, float thresholdLimit, Rectangle rectangle) where TPixel : struct, IPixel - => source.ApplyProcessor(new AdaptiveThresholdProcessor(upper, lower, threshold), rectangle); + => source.ApplyProcessor(new AdaptiveThresholdProcessor(upper, lower, thresholdLimit), rectangle); } } \ No newline at end of file From a8705176728a88804de6e6184723809b2a2cfae4 Mon Sep 17 00:00:00 2001 From: Simanto Rahman Date: Wed, 31 Oct 2018 12:46:26 -0230 Subject: [PATCH 14/58] Fixed a minor bug --- .../AdaptiveThresholdProcessor.cs | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs index 4fb97aa9f..2ad170e38 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs @@ -32,9 +32,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization /// /// Initializes a new instance of the class. /// - /// Threshold limit - public AdaptiveThresholdProcessor(float threshold) - : this(NamedColors.White, NamedColors.Black, threshold) + /// Threshold limit + public AdaptiveThresholdProcessor(float thresholdLimit) + : this(NamedColors.White, NamedColors.Black, thresholdLimit) { } @@ -48,14 +48,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization /// /// Color for upper threshold /// Color for lower threshold - /// Threshold limit - public AdaptiveThresholdProcessor(TPixel upper, TPixel lower, float threshold) + /// Threshold limit + public AdaptiveThresholdProcessor(TPixel upper, TPixel lower, float thresholdLimit) { this.pixelOpInstance = PixelOperations.Instance; this.Upper = upper; this.Lower = lower; - this.ThresholdLimit = threshold; + this.ThresholdLimit = thresholdLimit; } /// @@ -99,16 +99,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization this.pixelOpInstance.ToRgb24(source.GetPixelSpan(), tmpBuffer.GetSpan()); - for (int x = startX; x < endX; x++) + for (ushort x = startX; x < endX; x++) { Span rgbSpan = tmpBuffer.GetSpan(); ulong sum = 0; - for (int y = startY; y < endY; y++) + for (ushort y = startY; y < endY; y++) { ref Rgb24 rgb = ref rgbSpan[(width * y) + x]; sum += (ulong)(rgb.R + rgb.G + rgb.G); - if (x != 0) + if (x - startX != 0) { intImage[x - startX, y - startY] = intImage[x - startX - 1, y - startY] + sum; } @@ -129,9 +129,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization uint count = 0; long sum = 0; - for (int x = startX; x < endX; x++) + for (ushort x = startX; x < endX; x++) { - for (int y = rows.Min; y < rows.Max; y++) + for (ushort y = (ushort)rows.Min; y < (ushort)rows.Max; y++) { ref Rgb24 rgb = ref rgbSpan[(width * y) + x]; @@ -141,7 +141,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization y2 = (ushort)Math.Min(y - startY + clusterSize + 1, height - 1); count = (uint)((x2 - x1) * (y2 - y1)); - sum = (long)(intImage[x2, y2] - intImage[x1, y2] - intImage[x2, y1] + intImage[x1, y1]); + sum = (long)Math.Min(intImage[x2, y2] - intImage[x1, y2] - intImage[x2, y1] + intImage[x1, y1], long.MaxValue); if ((rgb.R + rgb.G + rgb.B) * count <= sum * this.ThresholdLimit) { From 9b1a7f1009d58f283c4f59d1047ea938d5c4c4cd Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 30 Jan 2020 13:39:55 +1100 Subject: [PATCH 15/58] Don't use ToArray() when we don't need to. --- Directory.Build.props | 28 ++++++++++++++-------------- src/ImageSharp/ImageExtensions.cs | 22 ++++++++++++++++++++-- 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 604153f97..def231a7a 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -31,27 +31,27 @@ - $(DefineConstants);SUPPORTS_MATHF;SUPPORTS_HASHCODE;SUPPORTS_EXTENDED_INTRINSICS;SUPPORTS_SPAN_STREAM;SUPPORTS_ENCODING_STRING;SUPPORTS_RUNTIME_INTRINSICS;SUPPORTS_CODECOVERAGE + $(DefineConstants);SUPPORTS_MATHF;SUPPORTS_HASHCODE;SUPPORTS_EXTENDED_INTRINSICS;SUPPORTS_SPAN_STREAM;SUPPORTS_ENCODING_STRING;SUPPORTS_RUNTIME_INTRINSICS;SUPPORTS_CODECOVERAGE;SUPPORTS_BASE64SPAN - $(DefineConstants);SUPPORTS_MATHF;SUPPORTS_HASHCODE;SUPPORTS_EXTENDED_INTRINSICS;SUPPORTS_SPAN_STREAM;SUPPORTS_ENCODING_STRING;SUPPORTS_CODECOVERAGE + $(DefineConstants);SUPPORTS_MATHF;SUPPORTS_HASHCODE;SUPPORTS_EXTENDED_INTRINSICS;SUPPORTS_SPAN_STREAM;SUPPORTS_ENCODING_STRING;SUPPORTS_CODECOVERAGE;SUPPORTS_BASE64SPAN - $(DefineConstants);SUPPORTS_MATHF;SUPPORTS_CODECOVERAGE + $(DefineConstants);SUPPORTS_MATHF;SUPPORTS_CODECOVERAGE;SUPPORTS_BASE64SPAN $(DefineConstants);SUPPORTS_MATHF;SUPPORTS_HASHCODE;SUPPORTS_SPAN_STREAM;SUPPORTS_ENCODING_STRING;SUPPORTS_CODECOVERAGE diff --git a/src/ImageSharp/ImageExtensions.cs b/src/ImageSharp/ImageExtensions.cs index 6cdc948d4..7f753c05a 100644 --- a/src/ImageSharp/ImageExtensions.cs +++ b/src/ImageSharp/ImageExtensions.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.Collections.Generic; using System.IO; using System.Text; @@ -115,8 +116,25 @@ namespace SixLabors.ImageSharp using (var stream = new MemoryStream()) { source.Save(stream, format); - stream.Flush(); - return $"data:{format.DefaultMimeType};base64,{Convert.ToBase64String(stream.ToArray())}"; + + // Always available. + stream.TryGetBuffer(out ArraySegment buffer); + +#if !SUPPORTS_BASE64SPAN + + byte[] sharedBuffer = ArrayPool.Shared.Rent(buffer.Count); + try + { + buffer.AsSpan().CopyTo(sharedBuffer); + return $"data:{format.DefaultMimeType};base64,{Convert.ToBase64String(sharedBuffer)}"; + } + finally + { + ArrayPool.Shared.Return(sharedBuffer); + } +#else + return $"data:{format.DefaultMimeType};base64,{Convert.ToBase64String(buffer)}"; +#endif } } } From c9e89b16e66e2e6f2c94960b0952e96970256455 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 14 Mar 2020 17:54:17 +1100 Subject: [PATCH 16/58] Fix edge detection and reenable entropy crop tests --- .../ConvolutionProcessor{TPixel}.cs | 6 +- .../EdgeDetector2DProcessor{TPixel}.cs | 5 ++ .../EdgeDetectorCompassProcessor{TPixel}.cs | 5 ++ .../EdgeDetectorProcessor{TPixel}.cs | 5 ++ .../Filters/OpaqueProcessor{TPixel}.cs | 67 +++++++++++++++++++ .../Processors/Transforms/EntropyCropTest.cs | 11 +-- tests/Images/External | 2 +- 7 files changed, 87 insertions(+), 14 deletions(-) create mode 100644 src/ImageSharp/Processing/Processors/Filters/OpaqueProcessor{TPixel}.cs diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs index a2c8fc1fb..8201b8e23 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs @@ -58,9 +58,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); var operation = new RowOperation(interest, targetPixels, source.PixelBuffer, this.KernelXY, this.Configuration, this.PreserveAlpha); ParallelRowIterator.IterateRows( - this.Configuration, - interest, - in operation); + this.Configuration, + interest, + in operation); Buffer2D.SwapOrCopyContent(source.PixelBuffer, targetPixels); } diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs index bdcf3cbc0..8bb60286a 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs @@ -52,6 +52,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// protected override void BeforeImageApply() { + using (IImageProcessor opaque = new OpaqueProcessor(this.Configuration, this.Source, this.SourceRectangle)) + { + opaque.Execute(); + } + if (this.Grayscale) { new GrayscaleBt709Processor(1F).Execute(this.Configuration, this.Source, this.SourceRectangle); diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs index 159c67b5c..1b07589b5 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs @@ -40,6 +40,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// protected override void BeforeImageApply() { + using (IImageProcessor opaque = new OpaqueProcessor(this.Configuration, this.Source, this.SourceRectangle)) + { + opaque.Execute(); + } + if (this.Grayscale) { new GrayscaleBt709Processor(1F).Execute(this.Configuration, this.Source, this.SourceRectangle); diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs index c49d03eb5..8ca548d97 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs @@ -43,6 +43,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// protected override void BeforeImageApply() { + using (IImageProcessor opaque = new OpaqueProcessor(this.Configuration, this.Source, this.SourceRectangle)) + { + opaque.Execute(); + } + if (this.Grayscale) { new GrayscaleBt709Processor(1F).Execute(this.Configuration, this.Source, this.SourceRectangle); diff --git a/src/ImageSharp/Processing/Processors/Filters/OpaqueProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Filters/OpaqueProcessor{TPixel}.cs new file mode 100644 index 000000000..95a099106 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Filters/OpaqueProcessor{TPixel}.cs @@ -0,0 +1,67 @@ +// 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.Advanced; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors.Filters +{ + internal sealed class OpaqueProcessor : ImageProcessor + where TPixel : unmanaged, IPixel + { + public OpaqueProcessor( + Configuration configuration, + Image source, + Rectangle sourceRectangle) + : base(configuration, source, sourceRectangle) + { + } + + protected override void OnFrameApply(ImageFrame source) + { + var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); + + var operation = new OpaqueRowOperation(this.Configuration, source, interest); + ParallelRowIterator.IterateRows(this.Configuration, interest, in operation); + } + + private readonly struct OpaqueRowOperation : IRowOperation + { + private readonly Configuration configuration; + private readonly ImageFrame target; + private readonly Rectangle bounds; + + [MethodImpl(InliningOptions.ShortMethod)] + public OpaqueRowOperation( + Configuration configuration, + ImageFrame target, + Rectangle bounds) + { + this.configuration = configuration; + this.target = target; + this.bounds = bounds; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void Invoke(int y, Span span) + { + Span targetRowSpan = this.target.GetPixelRowSpan(y).Slice(this.bounds.X); + PixelOperations.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, span.Length), span, PixelConversionModifiers.Scale); + ref Vector4 baseRef = ref MemoryMarshal.GetReference(span); + + for (int x = 0; x < this.bounds.Width; x++) + { + ref Vector4 v = ref Unsafe.Add(ref baseRef, x); + v.W = 1F; + } + + PixelOperations.Instance.FromVector4Destructive(this.configuration, span, targetRowSpan, PixelConversionModifiers.Scale); + } + } + } +} diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/EntropyCropTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/EntropyCropTest.cs index 5b0952f30..a04aef6bc 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/EntropyCropTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/EntropyCropTest.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; @@ -25,15 +25,6 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms public void EntropyCrop(TestImageProvider provider, float value) where TPixel : unmanaged, IPixel { - // The result dimensions of EntropyCrop may differ on .NET Core 3.1 because of unstable edge detection results. - // TODO: Re-enable this test case if we manage to improve stability. -#if SUPPORTS_RUNTIME_INTRINSICS - if (provider.SourceFileOrDescription.Contains(TestImages.Png.Ducky)) - { - return; - } -#endif - provider.RunValidatingProcessorTest(x => x.EntropyCrop(value), value, appendPixelTypeToFileName: false); } } diff --git a/tests/Images/External b/tests/Images/External index 1fea1ceab..d80955193 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 1fea1ceab89e87cc5f11376fa46164d3d27566c0 +Subproject commit d809551931858cd3873bad49d2fe915fddff7a26 From 81137a68244f421ac9395f7a3ab2b46a74fd26b2 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 14 Mar 2020 23:28:12 +1100 Subject: [PATCH 17/58] Add overloads. Fix #1144 --- src/ImageSharp/Image.FromBytes.cs | 40 ++++++++ src/ImageSharp/Image.FromFile.cs | 42 +++++++- src/ImageSharp/Image.FromStream.cs | 4 +- .../Image/ImageTests.Identify.cs | 99 +++++++++++++++++++ .../Image/ImageTests.ImageLoadTestBase.cs | 11 ++- 5 files changed, 189 insertions(+), 7 deletions(-) create mode 100644 tests/ImageSharp.Tests/Image/ImageTests.Identify.cs diff --git a/src/ImageSharp/Image.FromBytes.cs b/src/ImageSharp/Image.FromBytes.cs index 0850e2213..f1196ac97 100644 --- a/src/ImageSharp/Image.FromBytes.cs +++ b/src/ImageSharp/Image.FromBytes.cs @@ -37,6 +37,46 @@ namespace SixLabors.ImageSharp } } + /// + /// Reads the raw image information from the specified stream without fully decoding it. + /// + /// The byte array containing encoded image data to read the header from. + /// Thrown if the stream is not readable. + /// + /// The or null if suitable info detector not found. + /// + public static IImageInfo Identify(byte[] data) => Identify(data, out IImageFormat _); + + /// + /// Reads the raw image information from the specified stream without fully decoding it. + /// + /// The byte array containing encoded image data to read the header from. + /// The format type of the decoded image. + /// Thrown if the stream is not readable. + /// + /// The or null if suitable info detector not found. + /// + public static IImageInfo Identify(byte[] data, out IImageFormat format) => Identify(Configuration.Default, data, out format); + + /// + /// Reads the raw image information from the specified stream without fully decoding it. + /// + /// The configuration. + /// The byte array containing encoded image data to read the header from. + /// The format type of the decoded image. + /// Thrown if the stream is not readable. + /// + /// The or null if suitable info detector is not found. + /// + public static IImageInfo Identify(Configuration config, byte[] data, out IImageFormat format) + { + config ??= Configuration.Default; + using (var stream = new MemoryStream(data)) + { + return Identify(config, stream, out format); + } + } + /// /// Load a new instance of from the given encoded byte array. /// diff --git a/src/ImageSharp/Image.FromFile.cs b/src/ImageSharp/Image.FromFile.cs index 45ea378cf..bb26e4142 100644 --- a/src/ImageSharp/Image.FromFile.cs +++ b/src/ImageSharp/Image.FromFile.cs @@ -31,13 +31,53 @@ namespace SixLabors.ImageSharp /// The mime type or null if none found. public static IImageFormat DetectFormat(Configuration config, string filePath) { - config = config ?? Configuration.Default; + config ??= Configuration.Default; using (Stream file = config.FileSystem.OpenRead(filePath)) { return DetectFormat(config, file); } } + /// + /// Reads the raw image information from the specified stream without fully decoding it. + /// + /// The image file to open and to read the header from. + /// Thrown if the stream is not readable. + /// + /// The or null if suitable info detector not found. + /// + public static IImageInfo Identify(string filePath) => Identify(filePath, out IImageFormat _); + + /// + /// Reads the raw image information from the specified stream without fully decoding it. + /// + /// The image file to open and to read the header from. + /// The format type of the decoded image. + /// Thrown if the stream is not readable. + /// + /// The or null if suitable info detector not found. + /// + public static IImageInfo Identify(string filePath, out IImageFormat format) => Identify(Configuration.Default, filePath, out format); + + /// + /// Reads the raw image information from the specified stream without fully decoding it. + /// + /// The configuration. + /// The image file to open and to read the header from. + /// The format type of the decoded image. + /// Thrown if the stream is not readable. + /// + /// The or null if suitable info detector is not found. + /// + public static IImageInfo Identify(Configuration config, string filePath, out IImageFormat format) + { + config ??= Configuration.Default; + using (Stream file = config.FileSystem.OpenRead(filePath)) + { + return Identify(config, file, 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 d756ff7ac..95a71903a 100644 --- a/src/ImageSharp/Image.FromStream.cs +++ b/src/ImageSharp/Image.FromStream.cs @@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp => WithSeekableStream(config, stream, s => InternalDetectFormat(s, config)); /// - /// By reading the header on the provided stream this reads the raw image information. + /// Reads the raw image information from the specified stream without fully decoding it. /// /// The image stream to read the header from. /// Thrown if the stream is not readable. @@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp public static IImageInfo Identify(Stream stream) => Identify(stream, out IImageFormat _); /// - /// By reading the header on the provided stream this reads the raw image information. + /// Reads the raw image information from the specified stream without fully decoding it. /// /// The image stream to read the header from. /// The format type of the decoded image. diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Identify.cs b/tests/ImageSharp.Tests/Image/ImageTests.Identify.cs new file mode 100644 index 000000000..2be950407 --- /dev/null +++ b/tests/ImageSharp.Tests/Image/ImageTests.Identify.cs @@ -0,0 +1,99 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.IO; +using SixLabors.ImageSharp.Formats; + +using Xunit; + +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Tests +{ + public partial class ImageTests + { + /// + /// Tests the class. + /// + public class Identify : ImageLoadTestBase + { + private static readonly string ActualImagePath = TestFile.GetInputFileFullPath(TestImages.Bmp.F); + + private byte[] ActualImageBytes => TestFile.Create(TestImages.Bmp.F).Bytes; + + private byte[] ByteArray => this.DataStream.ToArray(); + + private IImageInfo LocalImageInfo => this.localImageInfoMock.Object; + + private IImageFormat LocalImageFormat => this.localImageFormatMock.Object; + + private static readonly IImageFormat ExpectedGlobalFormat = + Configuration.Default.ImageFormatsManager.FindFormatByFileExtension("bmp"); + + [Fact] + public void FromBytes_GlobalConfiguration() + { + IImageInfo info = Image.Identify(this.ActualImageBytes, out IImageFormat type); + + Assert.NotNull(info); + Assert.Equal(ExpectedGlobalFormat, type); + } + + [Fact] + public void FromBytes_CustomConfiguration() + { + IImageInfo info = Image.Identify(this.LocalConfiguration, this.ByteArray, out IImageFormat type); + + Assert.Equal(this.LocalImageInfo, info); + Assert.Equal(this.LocalImageFormat, type); + } + + [Fact] + public void FromFileSystemPath_GlobalConfiguration() + { + IImageInfo info = Image.Identify(ActualImagePath, out IImageFormat type); + + Assert.NotNull(info); + Assert.Equal(ExpectedGlobalFormat, type); + } + + [Fact] + public void FromFileSystemPath_CustomConfiguration() + { + IImageInfo info = Image.Identify(this.LocalConfiguration, this.MockFilePath, out IImageFormat type); + + Assert.Equal(this.LocalImageInfo, info); + Assert.Equal(this.LocalImageFormat, type); + } + + [Fact] + public void FromStream_GlobalConfiguration() + { + using (var stream = new MemoryStream(this.ActualImageBytes)) + { + IImageInfo info = Image.Identify(stream, out IImageFormat type); + + Assert.NotNull(info); + Assert.Equal(ExpectedGlobalFormat, type); + } + } + + [Fact] + public void FromStream_CustomConfiguration() + { + IImageInfo info = Image.Identify(this.LocalConfiguration, this.DataStream, out IImageFormat type); + + Assert.Equal(this.LocalImageInfo, info); + Assert.Equal(this.LocalImageFormat, type); + } + + [Fact] + public void WhenNoMatchingFormatFound_ReturnsNull() + { + IImageInfo info = Image.Identify(new Configuration(), this.DataStream, out IImageFormat type); + + Assert.Null(info); + Assert.Null(type); + } + } + } +} diff --git a/tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.cs b/tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.cs index dff83df26..d010f6023 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.cs @@ -26,6 +26,8 @@ namespace SixLabors.ImageSharp.Tests protected Mock localImageFormatMock; + protected Mock localImageInfoMock; + protected readonly string MockFilePath = Guid.NewGuid().ToString(); internal readonly Mock LocalFileSystemMock = new Mock(); @@ -53,9 +55,12 @@ namespace SixLabors.ImageSharp.Tests this.localStreamReturnImageRgba32 = new Image(1, 1); this.localStreamReturnImageAgnostic = new Image(1, 1); + this.localImageInfoMock = new Mock(); this.localImageFormatMock = new Mock(); - this.localDecoder = new Mock(); + var detector = new Mock(); + detector.Setup(x => x.Identify(It.IsAny(), It.IsAny())).Returns(this.localImageInfoMock.Object); + this.localDecoder = detector.As(); this.localMimeTypeDetector = new MockImageFormatDetector(this.localImageFormatMock.Object); this.localDecoder.Setup(x => x.Decode(It.IsAny(), It.IsAny())) @@ -80,9 +85,7 @@ namespace SixLabors.ImageSharp.Tests }) .Returns(this.localStreamReturnImageAgnostic); - this.LocalConfiguration = new Configuration - { - }; + this.LocalConfiguration = new Configuration(); this.LocalConfiguration.ImageFormatsManager.AddImageFormatDetector(this.localMimeTypeDetector); this.LocalConfiguration.ImageFormatsManager.SetDecoder(this.localImageFormatMock.Object, this.localDecoder.Object); From 87ef25fc87af1d1d62a2d589dc68a10a62d221f6 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 15 Mar 2020 00:29:54 +1100 Subject: [PATCH 18/58] Add Guard checks, don't pass null, --- src/ImageSharp/Image.FromBytes.cs | 95 +++++++++++++++--------------- src/ImageSharp/Image.FromFile.cs | 69 ++++++++++++---------- src/ImageSharp/Image.FromStream.cs | 64 ++++++++++---------- 3 files changed, 117 insertions(+), 111 deletions(-) diff --git a/src/ImageSharp/Image.FromBytes.cs b/src/ImageSharp/Image.FromBytes.cs index f1196ac97..06b05fe3c 100644 --- a/src/ImageSharp/Image.FromBytes.cs +++ b/src/ImageSharp/Image.FromBytes.cs @@ -26,14 +26,15 @@ namespace SixLabors.ImageSharp /// /// By reading the header on the provided byte array this calculates the images format. /// - /// The configuration. + /// The configuration. /// The byte array containing encoded image data to read the header from. /// The mime type or null if none found. - public static IImageFormat DetectFormat(Configuration config, byte[] data) + public static IImageFormat DetectFormat(Configuration configuration, byte[] data) { - using (var stream = new MemoryStream(data)) + Guard.NotNull(configuration, nameof(configuration)); + using (var stream = new MemoryStream(data, 0, data.Length, false, true)) { - return DetectFormat(config, stream); + return DetectFormat(configuration, stream); } } @@ -61,19 +62,19 @@ namespace SixLabors.ImageSharp /// /// Reads the raw image information from the specified stream without fully decoding it. /// - /// The configuration. + /// The configuration. /// The byte array containing encoded image data to read the header from. /// The format type of the decoded image. /// Thrown if the stream is not readable. /// /// The or null if suitable info detector is not found. /// - public static IImageInfo Identify(Configuration config, byte[] data, out IImageFormat format) + public static IImageInfo Identify(Configuration configuration, byte[] data, out IImageFormat format) { - config ??= Configuration.Default; - using (var stream = new MemoryStream(data)) + Guard.NotNull(configuration, nameof(configuration)); + using (var stream = new MemoryStream(data, 0, data.Length, false, true)) { - return Identify(config, stream, out format); + return Identify(configuration, stream, out format); } } @@ -108,33 +109,33 @@ namespace SixLabors.ImageSharp /// /// Load a new instance of from the given encoded byte array. /// - /// The configuration options. + /// The configuration options. /// The byte array containing encoded image data. /// The pixel format. /// A new . - public static Image Load(Configuration config, byte[] data) + public static Image Load(Configuration configuration, byte[] data) where TPixel : unmanaged, IPixel { using (var stream = new MemoryStream(data, 0, data.Length, false, true)) { - return Load(config, stream); + return Load(configuration, stream); } } /// /// Load a new instance of from the given encoded byte array. /// - /// The configuration options. + /// The configuration options. /// The byte array containing encoded image data. /// The of the decoded image. /// The pixel format. /// A new . - public static Image Load(Configuration config, byte[] data, out IImageFormat format) + public static Image Load(Configuration configuration, byte[] data, out IImageFormat format) where TPixel : unmanaged, IPixel { using (var stream = new MemoryStream(data, 0, data.Length, false, true)) { - return Load(config, stream, out format); + return Load(configuration, stream, out format); } } @@ -157,17 +158,17 @@ namespace SixLabors.ImageSharp /// /// Load a new instance of from the given encoded byte array. /// - /// The Configuration. + /// The Configuration. /// The byte array containing encoded image data. /// The decoder. /// The pixel format. /// A new . - public static Image Load(Configuration config, byte[] data, IImageDecoder decoder) + public static Image Load(Configuration configuration, byte[] data, IImageDecoder decoder) where TPixel : unmanaged, IPixel { using (var stream = new MemoryStream(data, 0, data.Length, false, true)) { - return Load(config, stream, decoder); + return Load(configuration, stream, decoder); } } @@ -184,18 +185,18 @@ namespace SixLabors.ImageSharp /// /// By reading the header on the provided byte array this calculates the images format. /// - /// The configuration. + /// The configuration. /// The byte array containing encoded image data to read the header from. /// The mime type or null if none found. - public static IImageFormat DetectFormat(Configuration config, ReadOnlySpan data) + public static IImageFormat DetectFormat(Configuration configuration, ReadOnlySpan data) { - int maxHeaderSize = config.MaxHeaderSize; + int maxHeaderSize = configuration.MaxHeaderSize; if (maxHeaderSize <= 0) { return null; } - foreach (IImageFormatDetector detector in config.ImageFormatsManager.FormatDetectors) + foreach (IImageFormatDetector detector in configuration.ImageFormatsManager.FormatDetectors) { IImageFormat f = detector.DetectFormat(data); @@ -243,18 +244,18 @@ namespace SixLabors.ImageSharp /// /// Load a new instance of from the given encoded byte span. /// - /// The configuration options. + /// The configuration options. /// The byte span containing encoded image data. /// The pixel format. /// A new . - public static unsafe Image Load(Configuration config, ReadOnlySpan data) + public static unsafe Image Load(Configuration configuration, ReadOnlySpan data) where TPixel : unmanaged, IPixel { fixed (byte* ptr = &data.GetPinnableReference()) { using (var stream = new UnmanagedMemoryStream(ptr, data.Length)) { - return Load(config, stream); + return Load(configuration, stream); } } } @@ -262,13 +263,13 @@ namespace SixLabors.ImageSharp /// /// Load a new instance of from the given encoded byte span. /// - /// The Configuration. + /// The Configuration. /// The byte span containing image data. /// The decoder. /// The pixel format. /// A new . public static unsafe Image Load( - Configuration config, + Configuration configuration, ReadOnlySpan data, IImageDecoder decoder) where TPixel : unmanaged, IPixel @@ -277,7 +278,7 @@ namespace SixLabors.ImageSharp { using (var stream = new UnmanagedMemoryStream(ptr, data.Length)) { - return Load(config, stream, decoder); + return Load(configuration, stream, decoder); } } } @@ -285,13 +286,13 @@ namespace SixLabors.ImageSharp /// /// Load a new instance of from the given encoded byte span. /// - /// The configuration options. + /// The configuration options. /// The byte span containing image data. /// The of the decoded image. /// The pixel format. /// A new . public static unsafe Image Load( - Configuration config, + Configuration configuration, ReadOnlySpan data, out IImageFormat format) where TPixel : unmanaged, IPixel @@ -300,7 +301,7 @@ namespace SixLabors.ImageSharp { using (var stream = new UnmanagedMemoryStream(ptr, data.Length)) { - return Load(config, stream, out format); + return Load(configuration, stream, out format); } } } @@ -325,38 +326,38 @@ namespace SixLabors.ImageSharp /// /// Load a new instance of from the given encoded byte array. /// - /// The config for the decoder. + /// The configuration for the decoder. /// The byte array containing encoded image data. /// The . - public static Image Load(Configuration config, byte[] data) => Load(config, data, out _); + public static Image Load(Configuration configuration, byte[] data) => Load(configuration, data, out _); /// /// Load a new instance of from the given encoded byte array. /// - /// The config for the decoder. + /// The configuration for the decoder. /// The byte array containing image data. /// The decoder. /// The . - public static Image Load(Configuration config, byte[] data, IImageDecoder decoder) + public static Image Load(Configuration configuration, byte[] data, IImageDecoder decoder) { using (var stream = new MemoryStream(data, 0, data.Length, false, true)) { - return Load(config, stream, decoder); + return Load(configuration, stream, decoder); } } /// /// Load a new instance of from the given encoded byte array. /// - /// The config for the decoder. + /// The configuration for the decoder. /// The byte array containing image data. /// The mime type of the decoded image. /// The . - public static Image Load(Configuration config, byte[] data, out IImageFormat format) + public static Image Load(Configuration configuration, byte[] data, out IImageFormat format) { using (var stream = new MemoryStream(data, 0, data.Length, false, true)) { - return Load(config, stream, out format); + return Load(configuration, stream, out format); } } @@ -388,20 +389,20 @@ namespace SixLabors.ImageSharp /// /// Decodes a new instance of from the given encoded byte span. /// - /// The configuration options. + /// The configuration options. /// The byte span containing image data. /// The . - public static Image Load(Configuration config, ReadOnlySpan data) => Load(config, data, out _); + public static Image Load(Configuration configuration, ReadOnlySpan data) => Load(configuration, data, out _); /// /// Load a new instance of from the given encoded byte span. /// - /// The Configuration. + /// The Configuration. /// The byte span containing image data. /// The decoder. /// The . public static unsafe Image Load( - Configuration config, + Configuration configuration, ReadOnlySpan data, IImageDecoder decoder) { @@ -409,7 +410,7 @@ namespace SixLabors.ImageSharp { using (var stream = new UnmanagedMemoryStream(ptr, data.Length)) { - return Load(config, stream, decoder); + return Load(configuration, stream, decoder); } } } @@ -417,12 +418,12 @@ namespace SixLabors.ImageSharp /// /// Load a new instance of from the given encoded byte span. /// - /// The configuration options. + /// The configuration options. /// The byte span containing image data. /// The of the decoded image.> /// The . public static unsafe Image Load( - Configuration config, + Configuration configuration, ReadOnlySpan data, out IImageFormat format) { @@ -430,7 +431,7 @@ namespace SixLabors.ImageSharp { using (var stream = new UnmanagedMemoryStream(ptr, data.Length)) { - return Load(config, stream, out format); + return Load(configuration, stream, out format); } } } diff --git a/src/ImageSharp/Image.FromFile.cs b/src/ImageSharp/Image.FromFile.cs index bb26e4142..1a9fa1546 100644 --- a/src/ImageSharp/Image.FromFile.cs +++ b/src/ImageSharp/Image.FromFile.cs @@ -26,15 +26,15 @@ namespace SixLabors.ImageSharp /// /// By reading the header on the provided file this calculates the images mime type. /// - /// The configuration. + /// The configuration. /// The image file to open and to read the header from. /// The mime type or null if none found. - public static IImageFormat DetectFormat(Configuration config, string filePath) + public static IImageFormat DetectFormat(Configuration configuration, string filePath) { - config ??= Configuration.Default; - using (Stream file = config.FileSystem.OpenRead(filePath)) + Guard.NotNull(configuration, nameof(configuration)); + using (Stream file = configuration.FileSystem.OpenRead(filePath)) { - return DetectFormat(config, file); + return DetectFormat(configuration, file); } } @@ -62,19 +62,19 @@ namespace SixLabors.ImageSharp /// /// Reads the raw image information from the specified stream without fully decoding it. /// - /// The configuration. + /// The configuration. /// The image file to open and to read the header from. /// The format type of the decoded image. /// Thrown if the stream is not readable. /// /// The or null if suitable info detector is not found. /// - public static IImageInfo Identify(Configuration config, string filePath, out IImageFormat format) + public static IImageInfo Identify(Configuration configuration, string filePath, out IImageFormat format) { - config ??= Configuration.Default; - using (Stream file = config.FileSystem.OpenRead(filePath)) + Guard.NotNull(configuration, nameof(configuration)); + using (Stream file = configuration.FileSystem.OpenRead(filePath)) { - return Identify(config, file, out format); + return Identify(configuration, file, out format); } } @@ -102,29 +102,30 @@ namespace SixLabors.ImageSharp /// /// Create a new instance of the class from the given file. /// - /// The config for the decoder. + /// The configuration for the decoder. /// The file path to the image. /// /// Thrown if the stream is not readable nor seekable. /// /// The . - public static Image Load(Configuration config, string path) => Load(config, path, out _); + public static Image Load(Configuration configuration, string path) => Load(configuration, path, out _); /// /// Create a new instance of the class from the given file. /// - /// The Configuration. + /// The Configuration. /// The file path to the image. /// The decoder. /// /// Thrown if the stream is not readable nor seekable. /// /// The . - public static Image Load(Configuration config, string path, IImageDecoder decoder) + public static Image Load(Configuration configuration, string path, IImageDecoder decoder) { - using (Stream stream = config.FileSystem.OpenRead(path)) + Guard.NotNull(configuration, nameof(configuration)); + using (Stream stream = configuration.FileSystem.OpenRead(path)) { - return Load(config, stream, decoder); + return Load(configuration, stream, decoder); } } @@ -173,26 +174,27 @@ namespace SixLabors.ImageSharp /// /// Create a new instance of the class from the given file. /// - /// The configuration options. + /// The configuration options. /// The file path to the image. /// /// Thrown if the stream is not readable nor seekable. /// /// The pixel format. /// A new . - public static Image Load(Configuration config, string path) + public static Image Load(Configuration configuration, string path) where TPixel : unmanaged, IPixel { - using (Stream stream = config.FileSystem.OpenRead(path)) + Guard.NotNull(configuration, nameof(configuration)); + using (Stream stream = configuration.FileSystem.OpenRead(path)) { - return Load(config, stream); + return Load(configuration, stream); } } /// /// Create a new instance of the class from the given file. /// - /// The configuration options. + /// The configuration options. /// The file path to the image. /// The mime type of the decoded image. /// @@ -200,12 +202,13 @@ namespace SixLabors.ImageSharp /// /// The pixel format. /// A new . - public static Image Load(Configuration config, string path, out IImageFormat format) + public static Image Load(Configuration configuration, string path, out IImageFormat format) where TPixel : unmanaged, IPixel { - using (Stream stream = config.FileSystem.OpenRead(path)) + Guard.NotNull(configuration, nameof(configuration)); + using (Stream stream = configuration.FileSystem.OpenRead(path)) { - return Load(config, stream, out format); + return Load(configuration, stream, out format); } } @@ -213,18 +216,19 @@ namespace SixLabors.ImageSharp /// Create a new instance of the class from the given file. /// The pixel type is selected by the decoder. /// - /// The configuration options. + /// 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) + public static Image Load(Configuration configuration, string path, out IImageFormat format) { - using (Stream stream = config.FileSystem.OpenRead(path)) + Guard.NotNull(configuration, nameof(configuration)); + using (Stream stream = configuration.FileSystem.OpenRead(path)) { - return Load(config, stream, out format); + return Load(configuration, stream, out format); } } @@ -247,7 +251,7 @@ namespace SixLabors.ImageSharp /// /// Create a new instance of the class from the given file. /// - /// The Configuration. + /// The Configuration. /// The file path to the image. /// The decoder. /// @@ -255,12 +259,13 @@ namespace SixLabors.ImageSharp /// /// The pixel format. /// A new . - public static Image Load(Configuration config, string path, IImageDecoder decoder) + public static Image Load(Configuration configuration, string path, IImageDecoder decoder) where TPixel : unmanaged, IPixel { - using (Stream stream = config.FileSystem.OpenRead(path)) + Guard.NotNull(configuration, nameof(configuration)); + using (Stream stream = configuration.FileSystem.OpenRead(path)) { - return Load(config, stream, decoder); + return Load(configuration, stream, decoder); } } } diff --git a/src/ImageSharp/Image.FromStream.cs b/src/ImageSharp/Image.FromStream.cs index 95a71903a..52d71409b 100644 --- a/src/ImageSharp/Image.FromStream.cs +++ b/src/ImageSharp/Image.FromStream.cs @@ -26,12 +26,12 @@ namespace SixLabors.ImageSharp /// /// By reading the header on the provided stream this calculates the images format type. /// - /// The configuration. + /// The configuration. /// The image stream to read the header from. /// Thrown if the stream is not readable. /// The format type or null if none found. - public static IImageFormat DetectFormat(Configuration config, Stream stream) - => WithSeekableStream(config, stream, s => InternalDetectFormat(s, config)); + public static IImageFormat DetectFormat(Configuration configuration, Stream stream) + => WithSeekableStream(configuration, stream, s => InternalDetectFormat(s, configuration)); /// /// Reads the raw image information from the specified stream without fully decoding it. @@ -57,16 +57,16 @@ namespace SixLabors.ImageSharp /// /// Reads the raw image information from the specified stream without fully decoding it. /// - /// The configuration. + /// The configuration. /// The image stream to read the information from. /// The format type of the decoded image. /// Thrown if the stream is not readable. /// /// The or null if suitable info detector is not found. /// - public static IImageInfo Identify(Configuration config, Stream stream, out IImageFormat format) + public static IImageInfo Identify(Configuration configuration, Stream stream, out IImageFormat format) { - (IImageInfo info, IImageFormat format) data = WithSeekableStream(config, stream, s => InternalIdentity(s, config ?? Configuration.Default)); + (IImageInfo info, IImageFormat format) data = WithSeekableStream(configuration, stream, s => InternalIdentity(s, configuration ?? Configuration.Default)); format = data.format; return data.info; @@ -108,24 +108,24 @@ namespace SixLabors.ImageSharp /// 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 configuration for the decoder. /// 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)); + public static Image Load(Configuration configuration, Stream stream, IImageDecoder decoder) => + WithSeekableStream(configuration, stream, s => decoder.Decode(configuration, s)); /// /// Decode a new instance of the class from the given stream. /// - /// The config for the decoder. + /// The configuration 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 _); + public static Image Load(Configuration configuration, Stream stream) => Load(configuration, stream, out _); /// /// Create a new instance of the class from the given stream. @@ -137,7 +137,7 @@ namespace SixLabors.ImageSharp /// A new .> public static Image Load(Stream stream) where TPixel : unmanaged, IPixel - => Load(null, stream); + => Load(Configuration.Default, stream); /// /// Create a new instance of the class from the given stream. @@ -150,7 +150,7 @@ namespace SixLabors.ImageSharp /// A new .> public static Image Load(Stream stream, out IImageFormat format) where TPixel : unmanaged, IPixel - => Load(null, stream, out format); + => Load(Configuration.Default, stream, out format); /// /// Create a new instance of the class from the given stream. @@ -168,45 +168,45 @@ namespace SixLabors.ImageSharp /// /// Create a new instance of the class from the given stream. /// - /// The Configuration. + /// The Configuration. /// 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) + public static Image Load(Configuration configuration, Stream stream, IImageDecoder decoder) where TPixel : unmanaged, IPixel - => WithSeekableStream(config, stream, s => decoder.Decode(config, s)); + => WithSeekableStream(configuration, stream, s => decoder.Decode(configuration, s)); /// /// Create a new instance of the class from the given stream. /// - /// The configuration options. + /// 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) + public static Image Load(Configuration configuration, Stream stream) where TPixel : unmanaged, IPixel - => Load(config, stream, out IImageFormat _); + => Load(configuration, stream, out IImageFormat _); /// /// Create a new instance of the class from the given stream. /// - /// The configuration options. + /// The configuration options. /// The stream containing image information. /// The format 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) + /// A new . + public static Image Load(Configuration configuration, Stream stream, out IImageFormat format) where TPixel : unmanaged, IPixel { - config = config ?? Configuration.Default; - (Image img, IImageFormat format) data = WithSeekableStream(config, stream, s => Decode(s, config)); + Guard.NotNull(configuration, nameof(configuration)); + (Image img, IImageFormat format) data = WithSeekableStream(configuration, stream, s => Decode(s, configuration)); format = data.format; @@ -218,7 +218,7 @@ namespace SixLabors.ImageSharp var sb = new StringBuilder(); sb.AppendLine("Image cannot be loaded. Available decoders:"); - foreach (KeyValuePair val in config.ImageFormatsManager.ImageDecoders) + foreach (KeyValuePair val in configuration.ImageFormatsManager.ImageDecoders) { sb.AppendLine($" - {val.Key.Name} : {val.Value.GetType().Name}"); } @@ -230,16 +230,16 @@ namespace SixLabors.ImageSharp /// Decode a new instance of the class from the given stream. /// The pixel format is selected by the decoder. /// - /// The configuration options. + /// The configuration options. /// The stream containing image information. /// The format 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) + public static Image Load(Configuration configuration, Stream stream, out IImageFormat format) { - config = config ?? Configuration.Default; - (Image img, IImageFormat format) data = WithSeekableStream(config, stream, s => Decode(s, config)); + Guard.NotNull(configuration, nameof(configuration)); + (Image img, IImageFormat format) data = WithSeekableStream(configuration, stream, s => Decode(s, configuration)); format = data.format; @@ -251,7 +251,7 @@ namespace SixLabors.ImageSharp var sb = new StringBuilder(); sb.AppendLine("Image cannot be loaded. Available decoders:"); - foreach (KeyValuePair val in config.ImageFormatsManager.ImageDecoders) + foreach (KeyValuePair val in configuration.ImageFormatsManager.ImageDecoders) { sb.AppendLine($" - {val.Key.Name} : {val.Value.GetType().Name}"); } @@ -259,7 +259,7 @@ namespace SixLabors.ImageSharp throw new UnknownImageFormatException(sb.ToString()); } - private static T WithSeekableStream(Configuration config, Stream stream, Func action) + private static T WithSeekableStream(Configuration configuration, Stream stream, Func action) { if (!stream.CanRead) { @@ -268,7 +268,7 @@ namespace SixLabors.ImageSharp if (stream.CanSeek) { - if (config.ReadOrigin == ReadOrigin.Begin) + if (configuration.ReadOrigin == ReadOrigin.Begin) { stream.Position = 0; } From f2aa2a7965f293f22559dcf9c0107b09b66dbeeb Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 24 Mar 2020 14:50:03 +0000 Subject: [PATCH 19/58] Replace Vector4.Clamp --- src/ImageSharp/ColorSpaces/Cmyk.cs | 2 +- .../Common/Helpers/DenseMatrixUtils.cs | 12 ++-- .../SimdUtils.FallbackIntrinsics128.cs | 4 +- src/ImageSharp/Common/Helpers/SimdUtils.cs | 2 +- .../{Vector4Utils.cs => Vector4Utilities.cs} | 18 +++++- .../Jpeg/Components/Block8x8F.Generated.cs | 32 +++++----- .../Jpeg/Components/Block8x8F.Generated.tt | 2 +- .../Formats/Jpeg/Components/Block8x8F.cs | 2 +- .../PixelImplementations/Argb32.cs | 2 +- .../PixelImplementations/Bgra32.cs | 2 +- .../PixelImplementations/Bgra4444.cs | 2 +- .../PixelImplementations/Bgra5551.cs | 2 +- .../PixelImplementations/Byte4.cs | 2 +- .../PixelFormats/PixelImplementations/L16.cs | 2 +- .../PixelFormats/PixelImplementations/L8.cs | 2 +- .../PixelFormats/PixelImplementations/La16.cs | 2 +- .../PixelFormats/PixelImplementations/La32.cs | 2 +- .../PixelImplementations/NormalizedByte4.cs | 2 +- .../PixelImplementations/NormalizedShort4.cs | 2 +- .../PixelImplementations/Rgb24.cs | 2 +- .../PixelImplementations/Rgb48.cs | 2 +- .../PixelImplementations/Rgba1010102.cs | 2 +- .../PixelImplementations/Rgba32.cs | 4 +- .../PixelImplementations/Rgba64.cs | 4 +- .../PixelImplementations/RgbaVector.cs | 2 +- .../PixelImplementations/Short4.cs | 2 +- .../PixelFormats/Utils/Vector4Converters.cs | 4 +- .../Convolution/BokehBlurProcessor{TPixel}.cs | 2 +- .../Filters/FilterProcessor{TPixel}.cs | 2 +- .../Linear/LinearTransformUtilities.cs | 6 +- .../General/BasicMath/ClampVector4.cs | 60 +++++++++++++++++++ .../Helpers/Vector4UtilsTests.cs | 8 +-- .../PixelOperations/PixelOperationsTests.cs | 24 ++++---- 33 files changed, 145 insertions(+), 75 deletions(-) rename src/ImageSharp/Common/Helpers/{Vector4Utils.cs => Vector4Utilities.cs} (85%) create mode 100644 tests/ImageSharp.Benchmarks/General/BasicMath/ClampVector4.cs diff --git a/src/ImageSharp/ColorSpaces/Cmyk.cs b/src/ImageSharp/ColorSpaces/Cmyk.cs index c2331c379..5229cf14f 100644 --- a/src/ImageSharp/ColorSpaces/Cmyk.cs +++ b/src/ImageSharp/ColorSpaces/Cmyk.cs @@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.ColorSpaces [MethodImpl(InliningOptions.ShortMethod)] public Cmyk(Vector4 vector) { - vector = Vector4.Clamp(vector, Min, Max); + vector = Vector4Utilities.FastClamp(vector, Min, Max); this.C = vector.X; this.M = vector.Y; this.Y = vector.Z; diff --git a/src/ImageSharp/Common/Helpers/DenseMatrixUtils.cs b/src/ImageSharp/Common/Helpers/DenseMatrixUtils.cs index bd25a7b44..462eeb302 100644 --- a/src/ImageSharp/Common/Helpers/DenseMatrixUtils.cs +++ b/src/ImageSharp/Common/Helpers/DenseMatrixUtils.cs @@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp ref Vector4 target = ref Unsafe.Add(ref targetRowRef, column); vector.W = target.W; - Vector4Utils.UnPremultiply(ref vector); + Vector4Utilities.UnPremultiply(ref vector); target = vector; } @@ -105,7 +105,7 @@ namespace SixLabors.ImageSharp out Vector4 vector); ref Vector4 target = ref Unsafe.Add(ref targetRowRef, column); - Vector4Utils.UnPremultiply(ref vector); + Vector4Utilities.UnPremultiply(ref vector); target = vector; } @@ -140,7 +140,7 @@ namespace SixLabors.ImageSharp { int offsetX = (sourceOffsetColumnBase + x - radiusX).Clamp(minColumn, maxColumn); var currentColor = sourceRowSpan[offsetX].ToVector4(); - Vector4Utils.Premultiply(ref currentColor); + Vector4Utilities.Premultiply(ref currentColor); vectorX += matrixX[y, x] * currentColor; vectorY += matrixY[y, x] * currentColor; @@ -193,7 +193,7 @@ namespace SixLabors.ImageSharp ref Vector4 target = ref Unsafe.Add(ref targetRowRef, column); vector.W = target.W; - Vector4Utils.UnPremultiply(ref vector); + Vector4Utilities.UnPremultiply(ref vector); target = vector; } @@ -238,7 +238,7 @@ namespace SixLabors.ImageSharp ref vector); ref Vector4 target = ref Unsafe.Add(ref targetRowRef, column); - Vector4Utils.UnPremultiply(ref vector); + Vector4Utilities.UnPremultiply(ref vector); target = vector; } @@ -270,7 +270,7 @@ namespace SixLabors.ImageSharp { int offsetX = (sourceOffsetColumnBase + x - radiusX).Clamp(minColumn, maxColumn); var currentColor = sourceRowSpan[offsetX].ToVector4(); - Vector4Utils.Premultiply(ref currentColor); + Vector4Utilities.Premultiply(ref currentColor); vector += matrix[y, x] * currentColor; } } diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.FallbackIntrinsics128.cs b/src/ImageSharp/Common/Helpers/SimdUtils.FallbackIntrinsics128.cs index aaacfdd85..6a93f9efc 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.FallbackIntrinsics128.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.FallbackIntrinsics128.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; @@ -125,8 +125,6 @@ namespace SixLabors.ImageSharp Vector4 s = Unsafe.Add(ref sBase, i); s *= maxBytes; s += half; - - // I'm not sure if Vector4.Clamp() is properly implemented with intrinsics. s = Vector4.Max(Vector4.Zero, s); s = Vector4.Min(maxBytes, s); diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.cs b/src/ImageSharp/Common/Helpers/SimdUtils.cs index b58ec900f..0dc45d887 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.cs @@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static Vector4 PseudoRound(this Vector4 v) { - var sign = Vector4.Clamp(v, new Vector4(-1), new Vector4(1)); + var sign = Vector4Utilities.FastClamp(v, new Vector4(-1), new Vector4(1)); return v + (sign * 0.5f); } diff --git a/src/ImageSharp/Common/Helpers/Vector4Utils.cs b/src/ImageSharp/Common/Helpers/Vector4Utilities.cs similarity index 85% rename from src/ImageSharp/Common/Helpers/Vector4Utils.cs rename to src/ImageSharp/Common/Helpers/Vector4Utilities.cs index 594a5ff10..02bb4d916 100644 --- a/src/ImageSharp/Common/Helpers/Vector4Utils.cs +++ b/src/ImageSharp/Common/Helpers/Vector4Utilities.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; @@ -11,8 +11,20 @@ namespace SixLabors.ImageSharp /// /// Utility methods for the struct. /// - internal static class Vector4Utils + internal static class Vector4Utilities { + /// + /// Restricts a vector between a minimum and a maximum value. + /// 5x Faster then . + /// + /// The vector to restrict. + /// The minimum value. + /// The maximum value. + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public static Vector4 FastClamp(Vector4 x, Vector4 min, Vector4 max) + => Vector4.Min(Vector4.Max(min, x), max); + /// /// Pre-multiplies the "x", "y", "z" components of a vector by its "w" component leaving the "w" component intact. /// @@ -107,4 +119,4 @@ namespace SixLabors.ImageSharp } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs index 033eedb92..8e14ed2c3 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs @@ -99,22 +99,22 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components var CMax4 = new Vector4(maximum); var 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); - this.V1L = Vector4.Clamp(this.V1L + COff4, CMin4, CMax4); - this.V1R = Vector4.Clamp(this.V1R + COff4, CMin4, CMax4); - this.V2L = Vector4.Clamp(this.V2L + COff4, CMin4, CMax4); - this.V2R = Vector4.Clamp(this.V2R + COff4, CMin4, CMax4); - this.V3L = Vector4.Clamp(this.V3L + COff4, CMin4, CMax4); - this.V3R = Vector4.Clamp(this.V3R + COff4, CMin4, CMax4); - this.V4L = Vector4.Clamp(this.V4L + COff4, CMin4, CMax4); - this.V4R = Vector4.Clamp(this.V4R + COff4, CMin4, CMax4); - this.V5L = Vector4.Clamp(this.V5L + COff4, CMin4, CMax4); - this.V5R = Vector4.Clamp(this.V5R + COff4, CMin4, CMax4); - this.V6L = Vector4.Clamp(this.V6L + COff4, CMin4, CMax4); - this.V6R = Vector4.Clamp(this.V6R + COff4, CMin4, CMax4); - this.V7L = Vector4.Clamp(this.V7L + COff4, CMin4, CMax4); - this.V7R = Vector4.Clamp(this.V7R + COff4, CMin4, CMax4); + this.V0L = Vector4Utilities.FastClamp(this.V0L + COff4, CMin4, CMax4); + this.V0R = Vector4Utilities.FastClamp(this.V0R + COff4, CMin4, CMax4); + this.V1L = Vector4Utilities.FastClamp(this.V1L + COff4, CMin4, CMax4); + this.V1R = Vector4Utilities.FastClamp(this.V1R + COff4, CMin4, CMax4); + this.V2L = Vector4Utilities.FastClamp(this.V2L + COff4, CMin4, CMax4); + this.V2R = Vector4Utilities.FastClamp(this.V2R + COff4, CMin4, CMax4); + this.V3L = Vector4Utilities.FastClamp(this.V3L + COff4, CMin4, CMax4); + this.V3R = Vector4Utilities.FastClamp(this.V3R + COff4, CMin4, CMax4); + this.V4L = Vector4Utilities.FastClamp(this.V4L + COff4, CMin4, CMax4); + this.V4R = Vector4Utilities.FastClamp(this.V4R + COff4, CMin4, CMax4); + this.V5L = Vector4Utilities.FastClamp(this.V5L + COff4, CMin4, CMax4); + this.V5R = Vector4Utilities.FastClamp(this.V5R + COff4, CMin4, CMax4); + this.V6L = Vector4Utilities.FastClamp(this.V6L + COff4, CMin4, CMax4); + this.V6R = Vector4Utilities.FastClamp(this.V6R + COff4, CMin4, CMax4); + this.V7L = Vector4Utilities.FastClamp(this.V7L + COff4, CMin4, CMax4); + this.V7R = Vector4Utilities.FastClamp(this.V7R + COff4, CMin4, CMax4); } /// diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt index 5370f2704..a1a6b0172 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt @@ -73,7 +73,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components for (int j = 0; j < 2; j++) { char side = j == 0 ? 'L' : 'R'; - Write($"this.V{i}{side} = Vector4.Clamp(this.V{i}{side} + COff4, CMin4, CMax4);\r\n"); + Write($"this.V{i}{side} = Vector4Utilities.FastClamp(this.V{i}{side} + COff4, CMin4, CMax4);\r\n"); } } PopIndent(); diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs index 868faceea..70a34ddcf 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs @@ -589,7 +589,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components private static Vector4 DivideRound(Vector4 dividend, Vector4 divisor) { // sign(dividend) = max(min(dividend, 1), -1) - var sign = Vector4.Clamp(dividend, NegativeOne, Vector4.One); + var sign = Vector4Utilities.FastClamp(dividend, NegativeOne, Vector4.One); // AlmostRound(dividend/divisor) = dividend/divisor + 0.5*sign(dividend) return (dividend / divisor) + (sign * Offset); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs index d5f4c54fb..52f6bcaa1 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs @@ -373,7 +373,7 @@ namespace SixLabors.ImageSharp.PixelFormats { vector *= MaxBytes; vector += Half; - vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); + vector = Vector4Utilities.FastClamp(vector, Vector4.Zero, MaxBytes); this.R = (byte)vector.X; this.G = (byte)vector.Y; diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs index 0f2991a35..40c187eb2 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs @@ -296,7 +296,7 @@ namespace SixLabors.ImageSharp.PixelFormats { vector *= MaxBytes; vector += Half; - vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); + vector = Vector4Utilities.FastClamp(vector, Vector4.Zero, MaxBytes); this.R = (byte)vector.X; this.G = (byte)vector.Y; diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs index f06831284..bbbf9145c 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs @@ -162,7 +162,7 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] private static ushort Pack(ref Vector4 vector) { - vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One); + vector = Vector4Utilities.FastClamp(vector, Vector4.Zero, Vector4.One); return (ushort)((((int)Math.Round(vector.W * 15F) & 0x0F) << 12) | (((int)Math.Round(vector.X * 15F) & 0x0F) << 8) | (((int)Math.Round(vector.Y * 15F) & 0x0F) << 4) diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs index 92f2a3f75..d10d10b47 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs @@ -163,7 +163,7 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] private static ushort Pack(ref Vector4 vector) { - vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One); + vector = Vector4Utilities.FastClamp(vector, Vector4.Zero, Vector4.One); return (ushort)( (((int)Math.Round(vector.X * 31F) & 0x1F) << 10) | (((int)Math.Round(vector.Y * 31F) & 0x1F) << 5) diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs index 728966b00..49b4f4138 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs @@ -171,7 +171,7 @@ namespace SixLabors.ImageSharp.PixelFormats const float Max = 255F; // Clamp the value between min and max values - vector = Vector4.Clamp(vector, Vector4.Zero, new Vector4(Max)); + vector = Vector4Utilities.FastClamp(vector, Vector4.Zero, new Vector4(Max)); uint byte4 = (uint)Math.Round(vector.X) & 0xFF; uint byte3 = ((uint)Math.Round(vector.Y) & 0xFF) << 0x8; diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs b/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs index 7235abd21..815ae6a4e 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs @@ -176,7 +176,7 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] internal void ConvertFromRgbaScaledVector4(Vector4 vector) { - vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One) * Max; + vector = Vector4Utilities.FastClamp(vector, Vector4.Zero, Vector4.One) * Max; this.PackedValue = ImageMaths.Get16BitBT709Luminance( vector.X, vector.Y, diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs b/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs index c622f1750..37a028db2 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs @@ -156,7 +156,7 @@ namespace SixLabors.ImageSharp.PixelFormats { vector *= MaxBytes; vector += Half; - vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); + vector = Vector4Utilities.FastClamp(vector, Vector4.Zero, MaxBytes); this.PackedValue = ImageMaths.Get8BitBT709Luminance((byte)vector.X, (byte)vector.Y, (byte)vector.Z); } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs b/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs index 66cb757c3..104c2be45 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs @@ -219,7 +219,7 @@ namespace SixLabors.ImageSharp.PixelFormats { vector *= MaxBytes; vector += Half; - vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); + vector = Vector4Utilities.FastClamp(vector, Vector4.Zero, MaxBytes); this.L = ImageMaths.Get8BitBT709Luminance((byte)vector.X, (byte)vector.Y, (byte)vector.Z); this.A = (byte)vector.W; } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs index 4885dae61..98a6cdae4 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs @@ -233,7 +233,7 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] internal void ConvertFromRgbaScaledVector4(Vector4 vector) { - vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One) * Max; + vector = Vector4Utilities.FastClamp(vector, Vector4.Zero, Vector4.One) * Max; this.L = ImageMaths.Get16BitBT709Luminance( vector.X, vector.Y, diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs index 3a4b92ff3..a7b350d55 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs @@ -174,7 +174,7 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] private static uint Pack(ref Vector4 vector) { - vector = Vector4.Clamp(vector, MinusOne, Vector4.One) * Half; + vector = Vector4Utilities.FastClamp(vector, MinusOne, Vector4.One) * Half; uint byte4 = ((uint)MathF.Round(vector.X) & 0xFF) << 0; uint byte3 = ((uint)MathF.Round(vector.Y) & 0xFF) << 8; diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs index 052e44f71..59433f17e 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs @@ -177,7 +177,7 @@ namespace SixLabors.ImageSharp.PixelFormats private static ulong Pack(ref Vector4 vector) { vector *= Max; - vector = Vector4.Clamp(vector, Min, Max); + vector = Vector4Utilities.FastClamp(vector, Min, Max); // Round rather than truncate. ulong word4 = ((ulong)MathF.Round(vector.X) & 0xFFFF) << 0x00; diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs index 5eb7b74b2..6e4839fed 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs @@ -254,7 +254,7 @@ namespace SixLabors.ImageSharp.PixelFormats { vector *= MaxBytes; vector += Half; - vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); + vector = Vector4Utilities.FastClamp(vector, Vector4.Zero, MaxBytes); this.R = (byte)vector.X; this.G = (byte)vector.Y; diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs index e494ff68e..dff8fe83f 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs @@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public void FromVector4(Vector4 vector) { - vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One) * Max; + vector = Vector4Utilities.FastClamp(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); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs index 2b5670778..7ca47f838 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs @@ -163,7 +163,7 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] private static uint Pack(ref Vector4 vector) { - vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One) * Multiplier; + vector = Vector4Utilities.FastClamp(vector, Vector4.Zero, Vector4.One) * Multiplier; return (uint)( (((int)Math.Round(vector.X) & 0x03FF) << 0) diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs index 8f67f2166..43ec095a1 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs @@ -452,7 +452,7 @@ namespace SixLabors.ImageSharp.PixelFormats { vector *= MaxBytes; vector += Half; - vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); + vector = Vector4Utilities.FastClamp(vector, Vector4.Zero, MaxBytes); return new Rgba32((byte)vector.X, (byte)vector.Y, (byte)vector.Z, (byte)vector.W); } @@ -491,7 +491,7 @@ namespace SixLabors.ImageSharp.PixelFormats { vector *= MaxBytes; vector += Half; - vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); + vector = Vector4Utilities.FastClamp(vector, Vector4.Zero, MaxBytes); this.R = (byte)vector.X; this.G = (byte)vector.Y; diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs index 88ef1dc98..8e5f8f093 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs @@ -127,7 +127,7 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public Rgba64(Vector4 vector) { - vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One) * Max; + vector = Vector4Utilities.FastClamp(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); @@ -209,7 +209,7 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public void FromVector4(Vector4 vector) { - vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One) * Max; + vector = Vector4Utilities.FastClamp(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); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs index 8a6bc94a7..8a6f882c3 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs @@ -111,7 +111,7 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public void FromVector4(Vector4 vector) { - vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One); + vector = Vector4Utilities.FastClamp(vector, Vector4.Zero, Vector4.One); this.R = vector.X; this.G = vector.Y; this.B = vector.Z; diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs index e709cd04f..135aa8d58 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs @@ -183,7 +183,7 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] private static ulong Pack(ref Vector4 vector) { - vector = Vector4.Clamp(vector, Min, Max); + vector = Vector4Utilities.FastClamp(vector, Min, Max); // Clamp the value between min and max values ulong word4 = ((ulong)Math.Round(vector.X) & 0xFFFF) << 0x00; diff --git a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.cs b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.cs index 447869a7d..ba676b3b8 100644 --- a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.cs +++ b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.cs @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils if (modifiers.IsDefined(PixelConversionModifiers.Premultiply)) { - Vector4Utils.Premultiply(vectors); + Vector4Utilities.Premultiply(vectors); } } @@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils { if (modifiers.IsDefined(PixelConversionModifiers.Premultiply)) { - Vector4Utils.UnPremultiply(vectors); + Vector4Utilities.UnPremultiply(vectors); } if (modifiers.IsDefined(PixelConversionModifiers.SRgbCompand)) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs index 493218cde..cf97751be 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs @@ -304,7 +304,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution for (int x = 0; x < this.bounds.Width; x++) { ref Vector4 v = ref Unsafe.Add(ref sourceRef, x); - var clamp = Vector4.Clamp(v, low, high); + var clamp = Vector4Utilities.FastClamp(v, low, high); v.X = MathF.Pow(clamp.X, this.inverseGamma); v.Y = MathF.Pow(clamp.Y, this.inverseGamma); v.Z = MathF.Pow(clamp.Z, this.inverseGamma); diff --git a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs index 7da4eb1b1..dee9d2ff6 100644 --- a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs @@ -74,7 +74,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters Span rowSpan = this.source.GetPixelRowSpan(y).Slice(this.startX, span.Length); PixelOperations.Instance.ToVector4(this.configuration, rowSpan, span); - Vector4Utils.Transform(span, ref Unsafe.AsRef(this.matrix)); + Vector4Utilities.Transform(span, ref Unsafe.AsRef(this.matrix)); PixelOperations.Instance.FromVector4Destructive(this.configuration, span, rowSpan); } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/LinearTransformUtilities.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/LinearTransformUtilities.cs index 04aaa1102..0a00cf8e9 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Linear/LinearTransformUtilities.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/LinearTransformUtilities.cs @@ -52,7 +52,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms MathF.Floor(maxXY.X), MathF.Floor(maxXY.Y)); - sourceExtents = Vector4.Clamp(sourceExtents, Vector4.Zero, maxSourceExtents); + sourceExtents = Vector4Utilities.FastClamp(sourceExtents, Vector4.Zero, maxSourceExtents); int left = (int)sourceExtents.X; int top = (int)sourceExtents.Y; @@ -78,13 +78,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms // Values are first premultiplied to prevent darkening of edge pixels. var current = sourcePixels[x, y].ToVector4(); - Vector4Utils.Premultiply(ref current); + Vector4Utilities.Premultiply(ref current); sum += current * xWeight * yWeight; } } // Reverse the premultiplication - Vector4Utils.UnPremultiply(ref sum); + Vector4Utilities.UnPremultiply(ref sum); targetRow[column] = sum; } diff --git a/tests/ImageSharp.Benchmarks/General/BasicMath/ClampVector4.cs b/tests/ImageSharp.Benchmarks/General/BasicMath/ClampVector4.cs new file mode 100644 index 000000000..8cbb31d21 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/BasicMath/ClampVector4.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Text; +using BenchmarkDotNet.Attributes; + +namespace SixLabors.ImageSharp.Benchmarks.General.BasicMath +{ + public class ClampVector4 + { + private readonly float min = -1.5f; + private readonly float max = 2.5f; + private static readonly float[] Values = { -10, -5, -3, -1.5f, -0.5f, 0f, 1f, 1.5f, 2.5f, 3, 10 }; + + [Benchmark(Baseline = true)] + public Vector4 UsingVectorClamp() + { + Vector4 acc = Vector4.Zero; + + for (int i = 0; i < Values.Length; i++) + { + acc += ClampUsingVectorClamp(Values[i], this.min, this.max); + } + + return acc; + } + + [Benchmark] + public Vector4 UsingVectorMinMax() + { + Vector4 acc = Vector4.Zero; + + for (int i = 0; i < Values.Length; i++) + { + acc += ClampUsingVectorMinMax(Values[i], this.min, this.max); + } + + return acc; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector4 ClampUsingVectorClamp(float x, float min, float max) + { + return Vector4.Clamp(new Vector4(x), new Vector4(min), new Vector4(max)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector4 ClampUsingVectorMinMax(float x, float min, float max) + { + return Vector4.Min(new Vector4(max), Vector4.Max(new Vector4(min), new Vector4(x))); + } + + // RESULTS + // | Method | Mean | Error | StdDev | Ratio | + // |------------------ |---------:|---------:|---------:|------:| + // | UsingVectorClamp | 75.21 ns | 1.572 ns | 4.057 ns | 1.00 | + // | UsingVectorMinMax | 15.35 ns | 0.356 ns | 0.789 ns | 0.20 | + } +} diff --git a/tests/ImageSharp.Tests/Helpers/Vector4UtilsTests.cs b/tests/ImageSharp.Tests/Helpers/Vector4UtilsTests.cs index af789a9b6..bc1ffda48 100644 --- a/tests/ImageSharp.Tests/Helpers/Vector4UtilsTests.cs +++ b/tests/ImageSharp.Tests/Helpers/Vector4UtilsTests.cs @@ -23,11 +23,11 @@ namespace SixLabors.ImageSharp.Tests.Helpers Vector4[] source = rnd.GenerateRandomVectorArray(length, 0, 1); Vector4[] expected = source.Select(v => { - Vector4Utils.Premultiply(ref v); + Vector4Utilities.Premultiply(ref v); return v; }).ToArray(); - Vector4Utils.Premultiply(source); + Vector4Utilities.Premultiply(source); Assert.Equal(expected, source, this.approximateFloatComparer); } @@ -42,11 +42,11 @@ namespace SixLabors.ImageSharp.Tests.Helpers Vector4[] source = rnd.GenerateRandomVectorArray(length, 0, 1); Vector4[] expected = source.Select(v => { - Vector4Utils.UnPremultiply(ref v); + Vector4Utilities.UnPremultiply(ref v); return v; }).ToArray(); - Vector4Utils.UnPremultiply(source); + Vector4Utilities.UnPremultiply(source); Assert.Equal(expected, source, this.approximateFloatComparer); } diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs index b2b39b590..9d48675f1 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs @@ -170,7 +170,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations { if (this.HasAlpha) { - Vector4Utils.Premultiply(ref v); + Vector4Utilities.Premultiply(ref v); } } @@ -178,7 +178,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations { if (this.HasAlpha) { - Vector4Utils.UnPremultiply(ref v); + Vector4Utilities.UnPremultiply(ref v); } } @@ -199,7 +199,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations { if (this.HasAlpha) { - Vector4Utils.Premultiply(ref v); + Vector4Utilities.Premultiply(ref v); } } @@ -207,7 +207,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations { if (this.HasAlpha) { - Vector4Utils.UnPremultiply(ref v); + Vector4Utilities.UnPremultiply(ref v); } } @@ -234,7 +234,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations if (this.HasAlpha) { - Vector4Utils.Premultiply(ref v); + Vector4Utilities.Premultiply(ref v); } } @@ -242,7 +242,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations { if (this.HasAlpha) { - Vector4Utils.UnPremultiply(ref v); + Vector4Utilities.UnPremultiply(ref v); } SRgbCompanding.Compress(ref v); @@ -349,12 +349,12 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations { void SourceAction(ref Vector4 v) { - Vector4Utils.UnPremultiply(ref v); + Vector4Utilities.UnPremultiply(ref v); } void ExpectedAction(ref Vector4 v) { - Vector4Utils.Premultiply(ref v); + Vector4Utilities.Premultiply(ref v); } TPixel[] source = CreatePixelTestData(count, (ref Vector4 v) => SourceAction(ref v)); @@ -372,12 +372,12 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations { void SourceAction(ref Vector4 v) { - Vector4Utils.UnPremultiply(ref v); + Vector4Utilities.UnPremultiply(ref v); } void ExpectedAction(ref Vector4 v) { - Vector4Utils.Premultiply(ref v); + Vector4Utilities.Premultiply(ref v); } TPixel[] source = CreateScaledPixelTestData(count, (ref Vector4 v) => SourceAction(ref v)); @@ -399,14 +399,14 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations { void SourceAction(ref Vector4 v) { - Vector4Utils.UnPremultiply(ref v); + Vector4Utilities.UnPremultiply(ref v); SRgbCompanding.Compress(ref v); } void ExpectedAction(ref Vector4 v) { SRgbCompanding.Expand(ref v); - Vector4Utils.Premultiply(ref v); + Vector4Utilities.Premultiply(ref v); } TPixel[] source = CreateScaledPixelTestData(count, (ref Vector4 v) => SourceAction(ref v)); From d8bc2d0ab72110c4594d4c08ba25f88ba6679804 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 24 Mar 2020 21:13:21 +0100 Subject: [PATCH 20/58] Add missing copyright note in ClampVector4 --- .../General/BasicMath/ClampVector4.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/ImageSharp.Benchmarks/General/BasicMath/ClampVector4.cs b/tests/ImageSharp.Benchmarks/General/BasicMath/ClampVector4.cs index 8cbb31d21..145b98b0f 100644 --- a/tests/ImageSharp.Benchmarks/General/BasicMath/ClampVector4.cs +++ b/tests/ImageSharp.Benchmarks/General/BasicMath/ClampVector4.cs @@ -1,8 +1,9 @@ -using System; -using System.Collections.Generic; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + using System.Numerics; using System.Runtime.CompilerServices; -using System.Text; + using BenchmarkDotNet.Attributes; namespace SixLabors.ImageSharp.Benchmarks.General.BasicMath From 70ea2d7b6a1647f4519e1057b26a1b41bd04e131 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 25 Mar 2020 09:25:37 +0000 Subject: [PATCH 21/58] Update based on Tanner's investigationl. --- .../Common/Helpers/SimdUtils.FallbackIntrinsics128.cs | 3 +-- src/ImageSharp/Common/Helpers/Vector4Utilities.cs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.FallbackIntrinsics128.cs b/src/ImageSharp/Common/Helpers/SimdUtils.FallbackIntrinsics128.cs index 6a93f9efc..f16c91b40 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.FallbackIntrinsics128.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.FallbackIntrinsics128.cs @@ -125,8 +125,7 @@ namespace SixLabors.ImageSharp Vector4 s = Unsafe.Add(ref sBase, i); s *= maxBytes; s += half; - s = Vector4.Max(Vector4.Zero, s); - s = Vector4.Min(maxBytes, s); + s = Vector4Utilities.FastClamp(s, Vector4.Zero, maxBytes); ref ByteVector4 d = ref Unsafe.Add(ref dBase, i); d.X = (byte)s.X; diff --git a/src/ImageSharp/Common/Helpers/Vector4Utilities.cs b/src/ImageSharp/Common/Helpers/Vector4Utilities.cs index 02bb4d916..9fb4eb790 100644 --- a/src/ImageSharp/Common/Helpers/Vector4Utilities.cs +++ b/src/ImageSharp/Common/Helpers/Vector4Utilities.cs @@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp /// The . [MethodImpl(InliningOptions.ShortMethod)] public static Vector4 FastClamp(Vector4 x, Vector4 min, Vector4 max) - => Vector4.Min(Vector4.Max(min, x), max); + => Vector4.Min(Vector4.Max(x, min), max); /// /// Pre-multiplies the "x", "y", "z" components of a vector by its "w" component leaving the "w" component intact. From 24b818f2ff264d4d246eb62258ff1cf16cf0a2d8 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 25 Mar 2020 16:21:26 +0000 Subject: [PATCH 22/58] Update ImageExtensions.cs --- src/ImageSharp/ImageExtensions.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ImageSharp/ImageExtensions.cs b/src/ImageSharp/ImageExtensions.cs index 24d2ad49b..3cb4f433e 100644 --- a/src/ImageSharp/ImageExtensions.cs +++ b/src/ImageSharp/ImageExtensions.cs @@ -2,7 +2,9 @@ // Licensed under the Apache License, Version 2.0. using System; +#if !SUPPORTS_BASE64SPAN using System.Buffers; +#endif using System.Collections.Generic; using System.IO; using System.Text; From 7e1ac921362b092e83719637e33cfe3bd0b2544c Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 25 Mar 2020 17:57:42 +0000 Subject: [PATCH 23/58] Simplify approach --- Directory.Build.props | 28 ++++++++++++++-------------- src/ImageSharp/ImageExtensions.cs | 27 +++++---------------------- 2 files changed, 19 insertions(+), 36 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 2f9863561..de3583a0c 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -31,27 +31,27 @@ - $(DefineConstants);SUPPORTS_MATHF;SUPPORTS_HASHCODE;SUPPORTS_EXTENDED_INTRINSICS;SUPPORTS_SPAN_STREAM;SUPPORTS_ENCODING_STRING;SUPPORTS_RUNTIME_INTRINSICS;SUPPORTS_CODECOVERAGE;SUPPORTS_BASE64SPAN + $(DefineConstants);SUPPORTS_MATHF;SUPPORTS_HASHCODE;SUPPORTS_EXTENDED_INTRINSICS;SUPPORTS_SPAN_STREAM;SUPPORTS_ENCODING_STRING;SUPPORTS_RUNTIME_INTRINSICS;SUPPORTS_CODECOVERAGE - $(DefineConstants);SUPPORTS_MATHF;SUPPORTS_HASHCODE;SUPPORTS_EXTENDED_INTRINSICS;SUPPORTS_SPAN_STREAM;SUPPORTS_ENCODING_STRING;SUPPORTS_CODECOVERAGE;SUPPORTS_BASE64SPAN + $(DefineConstants);SUPPORTS_MATHF;SUPPORTS_HASHCODE;SUPPORTS_EXTENDED_INTRINSICS;SUPPORTS_SPAN_STREAM;SUPPORTS_ENCODING_STRING;SUPPORTS_CODECOVERAGE - $(DefineConstants);SUPPORTS_MATHF;SUPPORTS_CODECOVERAGE;SUPPORTS_BASE64SPAN + $(DefineConstants);SUPPORTS_MATHF;SUPPORTS_CODECOVERAGE $(DefineConstants);SUPPORTS_MATHF;SUPPORTS_HASHCODE;SUPPORTS_SPAN_STREAM;SUPPORTS_ENCODING_STRING;SUPPORTS_CODECOVERAGE diff --git a/src/ImageSharp/ImageExtensions.cs b/src/ImageSharp/ImageExtensions.cs index 3cb4f433e..d7ac0b174 100644 --- a/src/ImageSharp/ImageExtensions.cs +++ b/src/ImageSharp/ImageExtensions.cs @@ -115,29 +115,12 @@ namespace SixLabors.ImageSharp public static string ToBase64String(this Image source, IImageFormat format) where TPixel : unmanaged, IPixel { - using (var stream = new MemoryStream()) - { - source.Save(stream, format); - - // Always available. - stream.TryGetBuffer(out ArraySegment buffer); + using var stream = new MemoryStream(); + source.Save(stream, format); -#if !SUPPORTS_BASE64SPAN - - byte[] sharedBuffer = ArrayPool.Shared.Rent(buffer.Count); - try - { - buffer.AsSpan().CopyTo(sharedBuffer); - return $"data:{format.DefaultMimeType};base64,{Convert.ToBase64String(sharedBuffer)}"; - } - finally - { - ArrayPool.Shared.Return(sharedBuffer); - } -#else - return $"data:{format.DefaultMimeType};base64,{Convert.ToBase64String(buffer)}"; -#endif - } + // Always available. + stream.TryGetBuffer(out ArraySegment buffer); + return $"data:{format.DefaultMimeType};base64,{Convert.ToBase64String(buffer.Array, 0, (int)stream.Length)}"; } } } From ae4a979b91d2526e2ec0287f2d8d8daa1a5b93b3 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 25 Mar 2020 17:58:48 +0000 Subject: [PATCH 24/58] Update Directory.Build.props --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index de3583a0c..12a4a5c2a 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -35,7 +35,7 @@ | SUPPORTS | MATHF | HASHCODE | EXTENDED_INTRINSICS | SPAN_STREAM | ENCODING_STRING | RUNTIME_INTRINSICS | CODECOVERAGE | +===================+=======+==========+=====================+=============+=================+====================+==============+ | netcoreapp3.1 | Y | Y | Y | Y | Y | Y | Y | - | netcoreapp2.1 | Y | Y | Y | Y | Y | N | Y | + | netcoreapp2.1 | Y | Y | Y | Y | Y | N | Y | | netcoreapp2.0 | Y | N | N | N | N | N | Y | | netstandard2.1 | Y | Y | N | Y | Y | N | Y | | netstandard2.0 | N | N | N | N | N | N | Y | From dc8094afa3531e54b67ba0da0276bd2c4da9bba4 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 25 Mar 2020 17:59:49 +0000 Subject: [PATCH 25/58] Update ImageExtensions.cs --- src/ImageSharp/ImageExtensions.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/ImageSharp/ImageExtensions.cs b/src/ImageSharp/ImageExtensions.cs index d7ac0b174..0bdbcc4ab 100644 --- a/src/ImageSharp/ImageExtensions.cs +++ b/src/ImageSharp/ImageExtensions.cs @@ -2,9 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -#if !SUPPORTS_BASE64SPAN -using System.Buffers; -#endif using System.Collections.Generic; using System.IO; using System.Text; From b5a861c54bd61f20dc507ac4692ee6b6254a2618 Mon Sep 17 00:00:00 2001 From: samsosa <32570890+samsosa@users.noreply.github.com> Date: Thu, 26 Mar 2020 00:49:56 +0100 Subject: [PATCH 26/58] Simple copy & paste error fixed --- src/ImageSharp/Common/Helpers/ImageMaths.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Common/Helpers/ImageMaths.cs b/src/ImageSharp/Common/Helpers/ImageMaths.cs index 92430c915..fb1f88a2d 100644 --- a/src/ImageSharp/Common/Helpers/ImageMaths.cs +++ b/src/ImageSharp/Common/Helpers/ImageMaths.cs @@ -359,7 +359,7 @@ namespace SixLabors.ImageSharp } } - return height; + return width; } topLeft.Y = GetMinY(bitmap); From 0a4c911ee5e16ee12cc7910248ede53b9c6eabac Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Thu, 26 Mar 2020 09:58:49 +0100 Subject: [PATCH 27/58] Add image extension method for save as tga --- src/ImageSharp/Formats/Tga/ImageExtensions.cs | 36 +++++++++++++++++++ .../Formats/GeneralFormatTests.cs | 19 ++++++---- .../Formats/ImageFormatManagerTests.cs | 5 ++- 3 files changed, 52 insertions(+), 8 deletions(-) create mode 100644 src/ImageSharp/Formats/Tga/ImageExtensions.cs diff --git a/src/ImageSharp/Formats/Tga/ImageExtensions.cs b/src/ImageSharp/Formats/Tga/ImageExtensions.cs new file mode 100644 index 000000000..286f04a22 --- /dev/null +++ b/src/ImageSharp/Formats/Tga/ImageExtensions.cs @@ -0,0 +1,36 @@ +// 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.Tga; + +namespace SixLabors.ImageSharp +{ + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Saves the image to the given stream with the tga format. + /// + /// The image this method extends. + /// The stream to save the image to. + /// Thrown if the stream is null. + public static void SaveAsTga(this Image source, Stream stream) => SaveAsTga(source, stream, null); + + /// + /// Saves the image to the given stream with the tga 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 SaveAsTga(this Image source, Stream stream, TgaEncoder encoder) => + source.Save( + stream, + encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TgaFormat.Instance)); + } +} diff --git a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs index e6b6e43c1..ca236e914 100644 --- a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs +++ b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs @@ -15,7 +15,7 @@ using SixLabors.ImageSharp.Processing.Processors.Quantization; using Xunit; -namespace SixLabors.ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests.Formats { public class GeneralFormatTests : FileTestBase { @@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Tests { using (Image image = file.CreateRgba32Image()) { - string filename = path + "/" + file.FileNameWithoutExtension + ".txt"; + string filename = Path.Combine(path, $"{file.FileNameWithoutExtension}.txt"); File.WriteAllText(filename, image.ToBase64String(PngFormat.Instance)); } } @@ -56,7 +56,7 @@ namespace SixLabors.ImageSharp.Tests { using (Image image = file.CreateRgba32Image()) { - image.Save($"{path}/{file.FileName}"); + image.Save(Path.Combine(path, file.FileName)); } } } @@ -103,25 +103,30 @@ namespace SixLabors.ImageSharp.Tests { using (Image image = file.CreateRgba32Image()) { - using (FileStream output = File.OpenWrite($"{path}/{file.FileNameWithoutExtension}.bmp")) + using (FileStream output = File.OpenWrite(Path.Combine(path, $"{file.FileNameWithoutExtension}.bmp"))) { image.SaveAsBmp(output); } - using (FileStream output = File.OpenWrite($"{path}/{file.FileNameWithoutExtension}.jpg")) + using (FileStream output = File.OpenWrite(Path.Combine(path, $"{file.FileNameWithoutExtension}.jpg"))) { image.SaveAsJpeg(output); } - using (FileStream output = File.OpenWrite($"{path}/{file.FileNameWithoutExtension}.png")) + using (FileStream output = File.OpenWrite(Path.Combine(path, $"{file.FileNameWithoutExtension}.png"))) { image.SaveAsPng(output); } - using (FileStream output = File.OpenWrite($"{path}/{file.FileNameWithoutExtension}.gif")) + using (FileStream output = File.OpenWrite(Path.Combine(path, $"{file.FileNameWithoutExtension}.gif"))) { image.SaveAsGif(output); } + + using (FileStream output = File.OpenWrite(Path.Combine(path, $"{file.FileNameWithoutExtension}.tga"))) + { + image.SaveAsTga(output); + } } } } diff --git a/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs b/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs index d011a6330..9dba7179d 100644 --- a/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs +++ b/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs @@ -10,10 +10,11 @@ using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.Formats.Tga; using SixLabors.ImageSharp.PixelFormats; using Xunit; -namespace SixLabors.ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests.Formats { public class ImageFormatManagerTests { @@ -34,11 +35,13 @@ namespace SixLabors.ImageSharp.Tests Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType().Count()); Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType().Count()); Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType().Count()); + Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType().Count()); Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType().Count()); Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType().Count()); Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType().Count()); Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType().Count()); + Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType().Count()); } [Fact] From 2e6e22dec7d68e33a9f71b849811a6b91396d406 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Thu, 26 Mar 2020 12:38:32 +0100 Subject: [PATCH 28/58] Add unit test for #1154: entropy crop on white image should not crop --- .../Processors/Transforms/EntropyCropTest.cs | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/EntropyCropTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/EntropyCropTest.cs index a04aef6bc..6ba795e56 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/EntropyCropTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/EntropyCropTest.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 SixLabors.ImageSharp.Processing; using Xunit; @@ -27,5 +26,24 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms { provider.RunValidatingProcessorTest(x => x.EntropyCrop(value), value, appendPixelTypeToFileName: false); } + + [Theory] + [WithBlankImages(40, 30, PixelTypes.Rgba32)] + [WithBlankImages(30, 40, PixelTypes.Rgba32)] + public void Entropy_WillNotCropWhiteImage(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + // arrange + using Image image = provider.GetImage(); + var expectedHeight = image.Height; + var expectedWidth = image.Width; + + // act + image.Mutate(img => img.EntropyCrop()); + + // assert + Assert.Equal(image.Width, expectedWidth); + Assert.Equal(image.Height, expectedHeight); + } } } From 003640b21041fe5277babd89832c019cf0839291 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Thu, 26 Mar 2020 19:52:57 +0100 Subject: [PATCH 29/58] Respect alpha channel bits from image descriptor during tga decoding --- src/ImageSharp/Formats/Tga/TgaDecoder.cs | 1 - src/ImageSharp/Formats/Tga/TgaDecoderCore.cs | 138 +++++++++++++----- src/ImageSharp/Formats/Tga/TgaMetadata.cs | 5 + .../Formats/Tga/TgaDecoderTests.cs | 15 ++ tests/ImageSharp.Tests/TestImages.cs | 4 + tests/Images/Input/Tga/16bit_noalphabits.tga | 3 + .../Input/Tga/16bit_rle_noalphabits.tga | 3 + tests/Images/Input/Tga/32bit_noalphabits.tga | 3 + .../Input/Tga/32bit_rle_no_alphabits.tga | 3 + 9 files changed, 138 insertions(+), 37 deletions(-) create mode 100644 tests/Images/Input/Tga/16bit_noalphabits.tga create mode 100644 tests/Images/Input/Tga/16bit_rle_noalphabits.tga create mode 100644 tests/Images/Input/Tga/32bit_noalphabits.tga create mode 100644 tests/Images/Input/Tga/32bit_rle_no_alphabits.tga diff --git a/src/ImageSharp/Formats/Tga/TgaDecoder.cs b/src/ImageSharp/Formats/Tga/TgaDecoder.cs index 2249c86bf..c3b8526ce 100644 --- a/src/ImageSharp/Formats/Tga/TgaDecoder.cs +++ b/src/ImageSharp/Formats/Tga/TgaDecoder.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; using System.IO; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; diff --git a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs index ead004003..bba04f98a 100644 --- a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs +++ b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs @@ -12,6 +12,9 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Tga { + /// + /// Performs the tga decoding operation. + /// internal sealed class TgaDecoderCore { /// @@ -49,6 +52,11 @@ namespace SixLabors.ImageSharp.Formats.Tga /// private readonly ITgaDecoderOptions options; + /// + /// Indicates whether there is a alpha channel present. + /// + private bool hasAlpha; + /// /// Initializes a new instance of the class. /// @@ -89,7 +97,7 @@ namespace SixLabors.ImageSharp.Formats.Tga TgaThrowHelper.ThrowNotSupportedException($"Unknown tga colormap type {this.fileHeader.ColorMapType} found"); } - if (this.fileHeader.Width == 0 || this.fileHeader.Height == 0) + if (this.fileHeader.Width is 0 || this.fileHeader.Height is 0) { throw new UnknownImageFormatException("Width or height cannot be 0"); } @@ -199,7 +207,7 @@ namespace SixLabors.ImageSharp.Formats.Tga break; default: - TgaThrowHelper.ThrowNotSupportedException("Does not support this kind of tga files."); + TgaThrowHelper.ThrowNotSupportedException("ImageSharp does not support this kind of tga files."); break; } @@ -241,9 +249,13 @@ namespace SixLabors.ImageSharp.Formats.Tga { int colorIndex = rowSpan[x]; - // Set alpha value to 1, to treat it as opaque for Bgra5551. Bgra5551 bgra = Unsafe.As(ref palette[colorIndex * colorMapPixelSizeInBytes]); - bgra.PackedValue = (ushort)(bgra.PackedValue | 0x8000); + if (!this.hasAlpha) + { + // Set alpha value to 1, to treat it as opaque for Bgra5551. + bgra.PackedValue = (ushort)(bgra.PackedValue | 0x8000); + } + color.FromBgra5551(bgra); pixelRow[x] = color; } @@ -291,6 +303,7 @@ namespace SixLabors.ImageSharp.Formats.Tga using (IMemoryOwner buffer = this.memoryAllocator.Allocate(width * height * bytesPerPixel, AllocationOptions.Clean)) { TPixel color = default; + var alphaBits = this.tgaMetadata.AlphaChannelBits; Span bufferSpan = buffer.GetSpan(); this.UncompressRle(width, height, bufferSpan, bytesPerPixel: 1); @@ -308,16 +321,30 @@ namespace SixLabors.ImageSharp.Formats.Tga color.FromL8(Unsafe.As(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes])); break; case 2: - // Set alpha value to 1, to treat it as opaque for Bgra5551. + Bgra5551 bgra = Unsafe.As(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes]); - bgra.PackedValue = (ushort)(bgra.PackedValue | 0x8000); + if (!this.hasAlpha) + { + // Set alpha value to 1, to treat it as opaque for Bgra5551. + bgra.PackedValue = (ushort)(bgra.PackedValue | 0x8000); + } + color.FromBgra5551(bgra); break; case 3: color.FromBgr24(Unsafe.As(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes])); break; case 4: - color.FromBgra32(Unsafe.As(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes])); + if (this.hasAlpha) + { + color.FromBgra32(Unsafe.As(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes])); + } + else + { + var alpha = alphaBits is 0 ? byte.MaxValue : bufferSpan[idx + 3]; + color.FromBgra32(new Bgra32(bufferSpan[idx + 2], bufferSpan[idx + 1], bufferSpan[idx], (byte)alpha)); + } + break; } @@ -345,11 +372,7 @@ namespace SixLabors.ImageSharp.Formats.Tga this.currentStream.Read(row); int newY = Invert(y, height, inverted); Span pixelSpan = pixels.GetRowSpan(newY); - PixelOperations.Instance.FromL8Bytes( - this.configuration, - row.GetSpan(), - pixelSpan, - width); + PixelOperations.Instance.FromL8Bytes(this.configuration, row.GetSpan(), pixelSpan, width); } } } @@ -372,19 +395,18 @@ namespace SixLabors.ImageSharp.Formats.Tga this.currentStream.Read(row); Span rowSpan = row.GetSpan(); - // We need to set each alpha component value to fully opaque. - for (int x = 1; x < rowSpan.Length; x += 2) + if (!this.hasAlpha) { - rowSpan[x] = (byte)(rowSpan[x] | (1 << 7)); + // We need to set the alpha component value to fully opaque. + for (int x = 1; x < rowSpan.Length; x += 2) + { + rowSpan[x] = (byte)(rowSpan[x] | (1 << 7)); + } } int newY = Invert(y, height, inverted); Span pixelSpan = pixels.GetRowSpan(newY); - PixelOperations.Instance.FromBgra5551Bytes( - this.configuration, - rowSpan, - pixelSpan, - width); + PixelOperations.Instance.FromBgra5551Bytes(this.configuration, rowSpan, pixelSpan, width); } } } @@ -407,11 +429,7 @@ namespace SixLabors.ImageSharp.Formats.Tga this.currentStream.Read(row); int newY = Invert(y, height, inverted); Span pixelSpan = pixels.GetRowSpan(newY); - PixelOperations.Instance.FromBgr24Bytes( - this.configuration, - row.GetSpan(), - pixelSpan, - width); + PixelOperations.Instance.FromBgr24Bytes(this.configuration, row.GetSpan(), pixelSpan, width); } } } @@ -427,18 +445,41 @@ namespace SixLabors.ImageSharp.Formats.Tga private void ReadBgra32(int width, int height, Buffer2D pixels, bool inverted) where TPixel : unmanaged, IPixel { + if (this.tgaMetadata.AlphaChannelBits is 8) + { + using (IManagedByteBuffer row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 4, 0)) + { + for (int y = 0; y < height; y++) + { + this.currentStream.Read(row); + int newY = Invert(y, height, inverted); + Span pixelSpan = pixels.GetRowSpan(newY); + + PixelOperations.Instance.FromBgra32Bytes(this.configuration, row.GetSpan(), pixelSpan, width); + } + } + + return; + } + + TPixel color = default; + var alphaBits = this.tgaMetadata.AlphaChannelBits; using (IManagedByteBuffer row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 4, 0)) { for (int y = 0; y < height; y++) { this.currentStream.Read(row); int newY = Invert(y, height, inverted); - Span pixelSpan = pixels.GetRowSpan(newY); - PixelOperations.Instance.FromBgra32Bytes( - this.configuration, - row.GetSpan(), - pixelSpan, - width); + Span pixelRow = pixels.GetRowSpan(newY); + Span rowSpan = row.GetSpan(); + + for (int x = 0; x < width; x++) + { + int idx = x * 4; + var alpha = alphaBits is 0 ? byte.MaxValue : rowSpan[idx + 3]; + color.FromBgra32(new Bgra32(rowSpan[idx + 2], rowSpan[idx + 1], rowSpan[idx], (byte)alpha)); + pixelRow[x] = color; + } } } } @@ -456,6 +497,7 @@ namespace SixLabors.ImageSharp.Formats.Tga where TPixel : unmanaged, IPixel { TPixel color = default; + var alphaBits = this.tgaMetadata.AlphaChannelBits; using (IMemoryOwner buffer = this.memoryAllocator.Allocate(width * height * bytesPerPixel, AllocationOptions.Clean)) { Span bufferSpan = buffer.GetSpan(); @@ -474,15 +516,28 @@ namespace SixLabors.ImageSharp.Formats.Tga color.FromL8(Unsafe.As(ref bufferSpan[idx])); break; case 2: - // Set alpha value to 1, to treat it as opaque for Bgra5551. - bufferSpan[idx + 1] = (byte)(bufferSpan[idx + 1] | 128); + if (!this.hasAlpha) + { + // Set alpha value to 1, to treat it as opaque for Bgra5551. + bufferSpan[idx + 1] = (byte)(bufferSpan[idx + 1] | 128); + } + color.FromBgra5551(Unsafe.As(ref bufferSpan[idx])); break; case 3: color.FromBgr24(Unsafe.As(ref bufferSpan[idx])); break; case 4: - color.FromBgra32(Unsafe.As(ref bufferSpan[idx])); + if (this.hasAlpha) + { + color.FromBgra32(Unsafe.As(ref bufferSpan[idx])); + } + else + { + var alpha = alphaBits is 0 ? byte.MaxValue : bufferSpan[idx + 3]; + color.FromBgra32(new Bgra32(bufferSpan[idx + 2], bufferSpan[idx + 1], bufferSpan[idx], (byte)alpha)); + } + break; } @@ -524,7 +579,7 @@ namespace SixLabors.ImageSharp.Formats.Tga // The high bit of a run length packet is set to 1. int highBit = runLengthByte >> 7; - if (highBit == 1) + if (highBit is 1) { int runLength = runLengthByte & 127; this.currentStream.Read(pixel, 0, bytesPerPixel); @@ -577,7 +632,18 @@ namespace SixLabors.ImageSharp.Formats.Tga this.tgaMetadata = this.metadata.GetTgaMetadata(); this.tgaMetadata.BitsPerPixel = (TgaBitsPerPixel)this.fileHeader.PixelDepth; - // Bit at position 5 of the descriptor indicates, that the origin is top left instead of bottom right. + var alphaBits = this.fileHeader.ImageDescriptor & 0xf; + if (alphaBits != 0 && alphaBits != 1 && alphaBits != 8) + { + TgaThrowHelper.ThrowImageFormatException("Invalid alpha channel bits"); + } + + this.tgaMetadata.AlphaChannelBits = (byte)alphaBits; + this.hasAlpha = alphaBits > 0; + + // TODO: bits 4 and 5 describe the image origin. See spec page 9. bit 4 is currently ignored. + // Theoretically the origin could also be top right and bottom right. + // Bit at position 5 of the descriptor indicates, that the origin is top left instead of bottom left. if ((this.fileHeader.ImageDescriptor & (1 << 5)) != 0) { return true; diff --git a/src/ImageSharp/Formats/Tga/TgaMetadata.cs b/src/ImageSharp/Formats/Tga/TgaMetadata.cs index 4ce61d2e4..69dee768a 100644 --- a/src/ImageSharp/Formats/Tga/TgaMetadata.cs +++ b/src/ImageSharp/Formats/Tga/TgaMetadata.cs @@ -29,6 +29,11 @@ namespace SixLabors.ImageSharp.Formats.Tga /// public TgaBitsPerPixel BitsPerPixel { get; set; } = TgaBitsPerPixel.Pixel24; + /// + /// Gets or sets the the number of alpha bits per pixel. + /// + public byte AlphaChannelBits { get; set; } = 0; + /// public IDeepCloneable DeepClone() => new TgaMetadata(this); } diff --git a/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs index bcd98d714..ec2621e65 100644 --- a/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs @@ -198,6 +198,21 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } } + [Theory] + [WithFile(NoAlphaBits32Bit, PixelTypes.Rgba32)] + [WithFile(NoAlphaBits16Bit, PixelTypes.Rgba32)] + [WithFile(NoAlphaBits32BitRle, PixelTypes.Rgba32)] + [WithFile(NoAlphaBits16BitRle, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_WhenAlphaBitsNotSet(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + [Theory] [WithFile(Bit16, PixelTypes.Rgba32)] [WithFile(Bit24, PixelTypes.Rgba32)] diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 2e58ac970..db4c9a448 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -390,6 +390,10 @@ namespace SixLabors.ImageSharp.Tests public const string Bit32Rle = "Tga/targa_32bit_rle.tga"; public const string Bit16Pal = "Tga/targa_16bit_pal.tga"; public const string Bit24Pal = "Tga/targa_24bit_pal.tga"; + public const string NoAlphaBits16Bit = "Tga/16bit_noalphabits.tga"; + public const string NoAlphaBits16BitRle = "Tga/16bit_rle_noalphabits.tga"; + public const string NoAlphaBits32Bit = "Tga/32bit_noalphabits.tga"; + public const string NoAlphaBits32BitRle = "Tga/32bit_rle_no_alphabits.tga"; } } } diff --git a/tests/Images/Input/Tga/16bit_noalphabits.tga b/tests/Images/Input/Tga/16bit_noalphabits.tga new file mode 100644 index 000000000..cff4abf94 --- /dev/null +++ b/tests/Images/Input/Tga/16bit_noalphabits.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f7a71e04cb2c335fb46bb91c6bf71e32deafe6a65b701e9fbdb1f95ec69a432c +size 96818 diff --git a/tests/Images/Input/Tga/16bit_rle_noalphabits.tga b/tests/Images/Input/Tga/16bit_rle_noalphabits.tga new file mode 100644 index 000000000..b1bbb8c54 --- /dev/null +++ b/tests/Images/Input/Tga/16bit_rle_noalphabits.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4c605b2ef72f8e54530cb3f0922527ee2754adab8d158276931ec7e2842f2644 +size 138354 diff --git a/tests/Images/Input/Tga/32bit_noalphabits.tga b/tests/Images/Input/Tga/32bit_noalphabits.tga new file mode 100644 index 000000000..903eca459 --- /dev/null +++ b/tests/Images/Input/Tga/32bit_noalphabits.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0aea1128a1bd7477dfa0d007a1eba25907be24847284c48a5f9fbd61bcea3cf0 +size 61522 diff --git a/tests/Images/Input/Tga/32bit_rle_no_alphabits.tga b/tests/Images/Input/Tga/32bit_rle_no_alphabits.tga new file mode 100644 index 000000000..b21dad5e0 --- /dev/null +++ b/tests/Images/Input/Tga/32bit_rle_no_alphabits.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:98a198392bd527523f8649d6126af81e5a588ad7265dc3d48a1da7b5a6cb6ff7 +size 230578 From c6b5452aeb66bc7cb060537e06409edd3d30778c Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 27 Mar 2020 14:10:32 +0100 Subject: [PATCH 30/58] Set alpha channel bits in the image descriptor during tga encoding --- src/ImageSharp/Formats/Tga/TgaEncoderCore.cs | 20 +++++++++++++++-- .../Formats/Tga/TgaEncoderTests.cs | 22 +++++++++---------- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs b/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs index d5d7ce49e..94bd367aa 100644 --- a/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs +++ b/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs @@ -78,8 +78,24 @@ namespace SixLabors.ImageSharp.Formats.Tga imageType = this.compression is TgaCompression.RunLength ? TgaImageType.RleBlackAndWhite : TgaImageType.BlackAndWhite; } - // If compression is used, set bit 5 of the image descriptor to indicate an left top origin. - byte imageDescriptor = (byte)(this.compression is TgaCompression.RunLength ? 32 : 0); + byte imageDescriptor = 0; + if (this.compression is TgaCompression.RunLength) + { + // If compression is used, set bit 5 of the image descriptor to indicate a left top origin. + imageDescriptor |= 0x20; + } + + if (this.bitsPerPixel is TgaBitsPerPixel.Pixel32) + { + // Indicate, that 8 bit are used for the alpha channel. + imageDescriptor |= 0x8; + } + + if (this.bitsPerPixel is TgaBitsPerPixel.Pixel16) + { + // Indicate, that 1 bit is used for the alpha channel. + imageDescriptor |= 0x1; + } var fileHeader = new TgaFileHeader( idLength: 0, diff --git a/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs index f123370d1..00664de6e 100644 --- a/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs @@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga [Theory] [MemberData(nameof(TgaBitsPerPixelFiles))] - public void Encode_PreserveBitsPerPixel(string imagePath, TgaBitsPerPixel bmpBitsPerPixel) + public void TgaEncoder_PreserveBitsPerPixel(string imagePath, TgaBitsPerPixel bmpBitsPerPixel) { var options = new TgaEncoder(); @@ -55,7 +55,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga [Theory] [MemberData(nameof(TgaBitsPerPixelFiles))] - public void Encode_WithCompression_PreserveBitsPerPixel(string imagePath, TgaBitsPerPixel bmpBitsPerPixel) + public void TgaEncoder_WithCompression_PreserveBitsPerPixel(string imagePath, TgaBitsPerPixel bmpBitsPerPixel) { var options = new TgaEncoder() { @@ -80,52 +80,52 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga [Theory] [WithFile(Bit32, PixelTypes.Rgba32)] - public void Encode_Bit8_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel8) + public void TgaEncoder_Bit8_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel8) // Using tolerant comparer here. The results from magick differ slightly. Maybe a different ToGrey method is used. The image looks otherwise ok. where TPixel : unmanaged, IPixel => TestTgaEncoderCore(provider, bitsPerPixel, TgaCompression.None, useExactComparer: false, compareTolerance: 0.03f); [Theory] [WithFile(Bit32, PixelTypes.Rgba32)] - public void Encode_Bit16_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel16) + public void TgaEncoder_Bit16_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel16) where TPixel : unmanaged, IPixel => TestTgaEncoderCore(provider, bitsPerPixel, TgaCompression.None, useExactComparer: false); [Theory] [WithFile(Bit32, PixelTypes.Rgba32)] - public void Encode_Bit24_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel24) + public void TgaEncoder_Bit24_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel24) where TPixel : unmanaged, IPixel => TestTgaEncoderCore(provider, bitsPerPixel, TgaCompression.None); [Theory] [WithFile(Bit32, PixelTypes.Rgba32)] - public void Encode_Bit32_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel32) + public void TgaEncoder_Bit32_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel32) where TPixel : unmanaged, IPixel => TestTgaEncoderCore(provider, bitsPerPixel, TgaCompression.None); [Theory] [WithFile(Bit32, PixelTypes.Rgba32)] - public void Encode_Bit8_WithRunLengthEncoding_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel8) + public void TgaEncoder_Bit8_WithRunLengthEncoding_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel8) // Using tolerant comparer here. The results from magick differ slightly. Maybe a different ToGrey method is used. The image looks otherwise ok. where TPixel : unmanaged, IPixel => TestTgaEncoderCore(provider, bitsPerPixel, TgaCompression.RunLength, useExactComparer: false, compareTolerance: 0.03f); [Theory] [WithFile(Bit32, PixelTypes.Rgba32)] - public void Encode_Bit16_WithRunLengthEncoding_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel16) + public void TgaEncoder_Bit16_WithRunLengthEncoding_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel16) where TPixel : unmanaged, IPixel => TestTgaEncoderCore(provider, bitsPerPixel, TgaCompression.RunLength, useExactComparer: false); [Theory] [WithFile(Bit32, PixelTypes.Rgba32)] - public void Encode_Bit24_WithRunLengthEncoding_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel24) + public void TgaEncoder_Bit24_WithRunLengthEncoding_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel24) where TPixel : unmanaged, IPixel => TestTgaEncoderCore(provider, bitsPerPixel, TgaCompression.RunLength); [Theory] [WithFile(Bit32, PixelTypes.Rgba32)] - public void Encode_Bit32_WithRunLengthEncoding_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel32) + public void TgaEncoder_Bit32_WithRunLengthEncoding_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel32) where TPixel : unmanaged, IPixel => TestTgaEncoderCore(provider, bitsPerPixel, TgaCompression.RunLength); [Theory] [WithFile(Bit32, PixelTypes.Rgba32, TgaBitsPerPixel.Pixel32)] [WithFile(Bit24, PixelTypes.Rgba32, TgaBitsPerPixel.Pixel24)] - public void Encode_WorksWithDiscontiguousBuffers(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel) + public void TgaEncoder_WorksWithDiscontiguousBuffers(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel) where TPixel : unmanaged, IPixel { provider.LimitAllocatorBufferCapacity().InPixelsSqrt(100); From 81e95e5216c25ca00294c13e7b8a21ddb3e481a2 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 27 Mar 2020 16:49:04 +0100 Subject: [PATCH 31/58] Split tga alpha bits tests in 16 bit and 32 bit tests --- .../Formats/Tga/TgaDecoderTests.cs | 20 ++++++++++++++++--- tests/ImageSharp.Tests/TestImages.cs | 2 +- tests/Images/External | 2 +- ...noalphabits.tga => 32bit_no_alphabits.tga} | 0 4 files changed, 19 insertions(+), 5 deletions(-) rename tests/Images/Input/Tga/{32bit_noalphabits.tga => 32bit_no_alphabits.tga} (100%) diff --git a/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs index ec2621e65..767b3b954 100644 --- a/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs @@ -7,6 +7,7 @@ using SixLabors.ImageSharp.Formats.Tga; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using Xunit; // ReSharper disable InconsistentNaming @@ -199,11 +200,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } [Theory] - [WithFile(NoAlphaBits32Bit, PixelTypes.Rgba32)] [WithFile(NoAlphaBits16Bit, PixelTypes.Rgba32)] - [WithFile(NoAlphaBits32BitRle, PixelTypes.Rgba32)] [WithFile(NoAlphaBits16BitRle, PixelTypes.Rgba32)] - public void TgaDecoder_CanDecode_WhenAlphaBitsNotSet(TestImageProvider provider) + public void TgaDecoder_CanDecode_WhenAlphaBitsNotSet_16Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { using (Image image = provider.GetImage(TgaDecoder)) @@ -213,6 +212,21 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } } + [Theory] + [WithFile(NoAlphaBits32Bit, PixelTypes.Rgba32)] + [WithFile(NoAlphaBits32BitRle, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_WhenAlphaBitsNotSet(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + // Using here the reference output instead of the the reference decoder, + // because the reference decoder does not ignore the alpha data here. + image.DebugSave(provider); + image.CompareToReferenceOutput(ImageComparer.Exact, provider); + } + } + [Theory] [WithFile(Bit16, PixelTypes.Rgba32)] [WithFile(Bit24, PixelTypes.Rgba32)] diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index db4c9a448..3e2f4aa6f 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -392,7 +392,7 @@ namespace SixLabors.ImageSharp.Tests public const string Bit24Pal = "Tga/targa_24bit_pal.tga"; public const string NoAlphaBits16Bit = "Tga/16bit_noalphabits.tga"; public const string NoAlphaBits16BitRle = "Tga/16bit_rle_noalphabits.tga"; - public const string NoAlphaBits32Bit = "Tga/32bit_noalphabits.tga"; + public const string NoAlphaBits32Bit = "Tga/32bit_no_alphabits.tga"; public const string NoAlphaBits32BitRle = "Tga/32bit_rle_no_alphabits.tga"; } } diff --git a/tests/Images/External b/tests/Images/External index d80955193..985e050aa 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit d809551931858cd3873bad49d2fe915fddff7a26 +Subproject commit 985e050aa7ac11830ae7a178ca2283f8b6307e4c diff --git a/tests/Images/Input/Tga/32bit_noalphabits.tga b/tests/Images/Input/Tga/32bit_no_alphabits.tga similarity index 100% rename from tests/Images/Input/Tga/32bit_noalphabits.tga rename to tests/Images/Input/Tga/32bit_no_alphabits.tga From b656a9de35a1b868340296ecb59d459b56d348a5 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 27 Mar 2020 21:46:25 +0000 Subject: [PATCH 32/58] Use Buffer2D throughout. Fix #1141 --- src/ImageSharp/Advanced/IPixelSource.cs | 15 +- .../Common/Extensions/ComparableExtensions.cs | 6 +- src/ImageSharp/Formats/Gif/GifDecoderCore.cs | 49 +++-- src/ImageSharp/Formats/Gif/GifEncoderCore.cs | 3 +- src/ImageSharp/Formats/Gif/LzwDecoder.cs | 24 ++- src/ImageSharp/Formats/Gif/LzwEncoder.cs | 179 ++++++++---------- .../Quantization/IndexedImageFrame{TPixel}.cs | 25 ++- .../Quantization/QuantizeProcessor{TPixel}.cs | 7 +- .../Quantization/QuantizedImageTests.cs | 4 +- .../Quantization/WuQuantizerTests.cs | 38 ++-- 10 files changed, 172 insertions(+), 178 deletions(-) diff --git a/src/ImageSharp/Advanced/IPixelSource.cs b/src/ImageSharp/Advanced/IPixelSource.cs index d7162bc61..f609b15d3 100644 --- a/src/ImageSharp/Advanced/IPixelSource.cs +++ b/src/ImageSharp/Advanced/IPixelSource.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.Memory; @@ -6,6 +6,17 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Advanced { + /// + /// Encapsulates the basic properties and methods required to manipulate images. + /// + internal interface IPixelSource + { + /// + /// Gets the pixel buffer. + /// + Buffer2D PixelBuffer { get; } + } + /// /// Encapsulates the basic properties and methods required to manipulate images. /// @@ -18,4 +29,4 @@ namespace SixLabors.ImageSharp.Advanced /// Buffer2D PixelBuffer { get; } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Common/Extensions/ComparableExtensions.cs b/src/ImageSharp/Common/Extensions/ComparableExtensions.cs index 3c8570a2a..1fe2a24c7 100644 --- a/src/ImageSharp/Common/Extensions/ComparableExtensions.cs +++ b/src/ImageSharp/Common/Extensions/ComparableExtensions.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; @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp /// /// Restricts a to be within a specified range. /// - /// The The value to clamp. + /// The value to clamp. /// The minimum value. If value is less than min, min will be returned. /// The maximum value. If value is greater than max, max will be returned. /// @@ -137,4 +137,4 @@ namespace SixLabors.ImageSharp return value; } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index 02267de1a..7919bc148 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -353,7 +353,7 @@ namespace SixLabors.ImageSharp.Formats.Gif this.ReadImageDescriptor(); IManagedByteBuffer localColorTable = null; - IManagedByteBuffer indices = null; + Buffer2D indices = null; try { // Determine the color table for this frame. If there is a local one, use it otherwise use the global color table. @@ -364,11 +364,11 @@ namespace SixLabors.ImageSharp.Formats.Gif this.stream.Read(localColorTable.Array, 0, length); } - indices = this.configuration.MemoryAllocator.AllocateManagedByteBuffer(this.imageDescriptor.Width * this.imageDescriptor.Height, AllocationOptions.Clean); + indices = this.configuration.MemoryAllocator.Allocate2D(this.imageDescriptor.Width, this.imageDescriptor.Height, AllocationOptions.Clean); - this.ReadFrameIndices(this.imageDescriptor, indices.GetSpan()); + this.ReadFrameIndices(indices); ReadOnlySpan colorTable = MemoryMarshal.Cast((localColorTable ?? this.globalColorTable).GetSpan()); - this.ReadFrameColors(ref image, ref previousFrame, indices.GetSpan(), colorTable, this.imageDescriptor); + this.ReadFrameColors(ref image, ref previousFrame, indices, colorTable, this.imageDescriptor); // Skip any remaining blocks this.SkipBlock(); @@ -383,16 +383,13 @@ namespace SixLabors.ImageSharp.Formats.Gif /// /// Reads the frame indices marking the color to use for each pixel. /// - /// The . - /// The pixel array to write to. + /// The 2D pixel buffer to write to. [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ReadFrameIndices(in GifImageDescriptor imageDescriptor, Span indices) + private void ReadFrameIndices(Buffer2D indices) { int dataSize = this.stream.ReadByte(); - using (var lzwDecoder = new LzwDecoder(this.configuration.MemoryAllocator, this.stream)) - { - lzwDecoder.DecodePixels(imageDescriptor.Width, imageDescriptor.Height, dataSize, indices); - } + using var lzwDecoder = new LzwDecoder(this.configuration.MemoryAllocator, this.stream); + lzwDecoder.DecodePixels(dataSize, indices); } /// @@ -404,10 +401,9 @@ namespace SixLabors.ImageSharp.Formats.Gif /// The indexed pixels. /// The color table containing the available colors. /// The - private void ReadFrameColors(ref Image image, ref ImageFrame previousFrame, Span indices, ReadOnlySpan colorTable, in GifImageDescriptor descriptor) + private void ReadFrameColors(ref Image image, ref ImageFrame previousFrame, Buffer2D indices, ReadOnlySpan colorTable, in GifImageDescriptor descriptor) where TPixel : unmanaged, IPixel { - ref byte indicesRef = ref MemoryMarshal.GetReference(indices); int imageWidth = this.logicalScreenDescriptor.Width; int imageHeight = this.logicalScreenDescriptor.Height; @@ -440,13 +436,20 @@ namespace SixLabors.ImageSharp.Formats.Gif this.RestoreToBackground(imageFrame); } - int i = 0; int interlacePass = 0; // The interlace pass int interlaceIncrement = 8; // The interlacing line increment int interlaceY = 0; // The current interlaced line - - for (int y = descriptor.Top; y < descriptor.Top + descriptor.Height; y++) + int descriptorTop = descriptor.Top; + int descriptorBottom = descriptorTop + descriptor.Height; + int descriptorLeft = descriptor.Left; + int descriptorRight = descriptorLeft + descriptor.Width; + bool transFlag = this.graphicsControlExtension.TransparencyFlag; + byte transIndex = this.graphicsControlExtension.TransparencyIndex; + + for (int y = descriptorTop; y < descriptorBottom && y < imageHeight; y++) { + ref byte indicesRowRef = ref MemoryMarshal.GetReference(indices.GetRowSpan(y - descriptorTop)); + // Check if this image is interlaced. int writeY; // the target y offset to write to if (descriptor.InterlaceFlag) @@ -482,35 +485,29 @@ namespace SixLabors.ImageSharp.Formats.Gif } ref TPixel rowRef = ref MemoryMarshal.GetReference(imageFrame.GetPixelRowSpan(writeY)); - bool transFlag = this.graphicsControlExtension.TransparencyFlag; if (!transFlag) { // #403 The left + width value can be larger than the image width - for (int x = descriptor.Left; x < descriptor.Left + descriptor.Width && x < imageWidth; x++) + for (int x = descriptorLeft; x < descriptorRight && x < imageWidth; x++) { - int index = Unsafe.Add(ref indicesRef, i); + int index = Unsafe.Add(ref indicesRowRef, x - descriptorLeft); ref TPixel pixel = ref Unsafe.Add(ref rowRef, x); Rgb24 rgb = colorTable[index]; pixel.FromRgb24(rgb); - - i++; } } else { - byte transIndex = this.graphicsControlExtension.TransparencyIndex; - for (int x = descriptor.Left; x < descriptor.Left + descriptor.Width && x < imageWidth; x++) + for (int x = descriptorLeft; x < descriptorRight && x < imageWidth; x++) { - int index = Unsafe.Add(ref indicesRef, i); + int index = Unsafe.Add(ref indicesRowRef, x - descriptorLeft); if (transIndex != index) { ref TPixel pixel = ref Unsafe.Add(ref rowRef, x); Rgb24 rgb = colorTable[index]; pixel.FromRgb24(rgb); } - - i++; } } } diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs index 887540930..62410025c 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs @@ -6,6 +6,7 @@ using System.Buffers; using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; @@ -471,7 +472,7 @@ namespace SixLabors.ImageSharp.Formats.Gif where TPixel : unmanaged, IPixel { using var encoder = new LzwEncoder(this.memoryAllocator, (byte)this.bitDepth); - encoder.Encode(image.GetPixelBufferSpan(), stream); + encoder.Encode(((IPixelSource)image).PixelBuffer, stream); } } } diff --git a/src/ImageSharp/Formats/Gif/LzwDecoder.cs b/src/ImageSharp/Formats/Gif/LzwDecoder.cs index 0129db0e3..8289ee75b 100644 --- a/src/ImageSharp/Formats/Gif/LzwDecoder.cs +++ b/src/ImageSharp/Formats/Gif/LzwDecoder.cs @@ -65,15 +65,15 @@ namespace SixLabors.ImageSharp.Formats.Gif /// /// Decodes and decompresses all pixel indices from the stream. /// - /// The width of the pixel index array. - /// The height of the pixel index array. /// Size of the data. /// The pixel array to decode to. - public void DecodePixels(int width, int height, int dataSize, Span pixels) + public void DecodePixels(int dataSize, Buffer2D pixels) { Guard.MustBeLessThan(dataSize, int.MaxValue, nameof(dataSize)); // The resulting index table length. + int width = pixels.Width; + int height = pixels.Height; int length = width * height; // Calculate the clear code. The value of the clear code is 2 ^ dataSize @@ -105,17 +105,28 @@ namespace SixLabors.ImageSharp.Formats.Gif ref int prefixRef = ref MemoryMarshal.GetReference(this.prefix.GetSpan()); ref int suffixRef = ref MemoryMarshal.GetReference(this.suffix.GetSpan()); ref int pixelStackRef = ref MemoryMarshal.GetReference(this.pixelStack.GetSpan()); - ref byte pixelsRef = ref MemoryMarshal.GetReference(pixels); for (code = 0; code < clearCode; code++) { Unsafe.Add(ref suffixRef, code) = (byte)code; } - Span buffer = stackalloc byte[255]; + Span buffer = stackalloc byte[byte.MaxValue]; + int y = 0; + int x = 0; + int rowMax = width; + ref byte pixelsRowRef = ref MemoryMarshal.GetReference(pixels.GetRowSpan(y)); while (xyz < length) { + // Reset row reference. + if (xyz == rowMax) + { + x = 0; + pixelsRowRef = ref MemoryMarshal.GetReference(pixels.GetRowSpan(++y)); + rowMax = (y * width) + width; + } + if (top == 0) { if (bits < codeSize) @@ -209,7 +220,8 @@ namespace SixLabors.ImageSharp.Formats.Gif top--; // Clear missing pixels - Unsafe.Add(ref pixelsRef, xyz++) = (byte)Unsafe.Add(ref pixelStackRef, top); + xyz++; + Unsafe.Add(ref pixelsRowRef, x++) = (byte)Unsafe.Add(ref pixelStackRef, top); } } diff --git a/src/ImageSharp/Formats/Gif/LzwEncoder.cs b/src/ImageSharp/Formats/Gif/LzwEncoder.cs index 056076bf0..516b82396 100644 --- a/src/ImageSharp/Formats/Gif/LzwEncoder.cs +++ b/src/ImageSharp/Formats/Gif/LzwEncoder.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; @@ -41,13 +41,33 @@ namespace SixLabors.ImageSharp.Formats.Gif /// private const int HashSize = 5003; + /// + /// The amount to shift each code. + /// + private const int HashShift = 4; + /// /// Mask used when shifting pixel values /// private static readonly int[] Masks = { - 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, - 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF + 0b0, + 0b1, + 0b11, + 0b111, + 0b1111, + 0b11111, + 0b111111, + 0b1111111, + 0b11111111, + 0b111111111, + 0b1111111111, + 0b11111111111, + 0b111111111111, + 0b1111111111111, + 0b11111111111111, + 0b111111111111111, + 0b1111111111111111 }; /// @@ -80,16 +100,6 @@ namespace SixLabors.ImageSharp.Formats.Gif /// private readonly byte[] accumulators = new byte[256]; - /// - /// For dynamic table sizing - /// - private readonly int hsize = HashSize; - - /// - /// The current position within the pixelArray. - /// - private int position; - /// /// Number of bits/code /// @@ -177,15 +187,13 @@ namespace SixLabors.ImageSharp.Formats.Gif /// /// Encodes and compresses the indexed pixels to the stream. /// - /// The span of indexed pixels. + /// The 2D buffer of indexed pixels. /// The stream to write to. - public void Encode(ReadOnlySpan indexedPixels, Stream stream) + public void Encode(Buffer2D indexedPixels, Stream stream) { // Write "initial code size" byte stream.WriteByte((byte)this.initialCodeSize); - this.position = 0; - // Compress and write the pixel data this.Compress(indexedPixels, this.initialCodeSize + 1, stream); @@ -199,10 +207,7 @@ namespace SixLabors.ImageSharp.Formats.Gif /// The number of bits /// See [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int GetMaxcode(int bitCount) - { - return (1 << bitCount) - 1; - } + private static int GetMaxcode(int bitCount) => (1 << bitCount) - 1; /// /// Add a character to the end of the current packet, and if it is 254 characters, @@ -239,25 +244,16 @@ namespace SixLabors.ImageSharp.Formats.Gif /// Reset the code table. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ResetCodeTable() - { - this.hashTable.GetSpan().Fill(-1); - } + private void ResetCodeTable() => this.hashTable.GetSpan().Fill(-1); /// /// Compress the packets to the stream. /// - /// The span of indexed pixels. + /// The 2D buffer of indexed pixels. /// The initial bits. /// The stream to write to. - private void Compress(ReadOnlySpan indexedPixels, int initialBits, Stream stream) + private void Compress(Buffer2D indexedPixels, int initialBits, Stream stream) { - int fcode; - int c; - int ent; - int hsizeReg; - int hshift; - // Set up the globals: globalInitialBits - initial number of bits this.globalInitialBits = initialBits; @@ -265,92 +261,82 @@ namespace SixLabors.ImageSharp.Formats.Gif this.clearFlag = false; this.bitCount = this.globalInitialBits; this.maxCode = GetMaxcode(this.bitCount); - this.clearCode = 1 << (initialBits - 1); this.eofCode = this.clearCode + 1; this.freeEntry = this.clearCode + 2; + this.accumulatorCount = 0; // Clear packet - this.accumulatorCount = 0; // clear packet - - ent = this.NextPixel(indexedPixels); - - // TODO: PERF: It looks like hshift could be calculated once statically. - hshift = 0; - for (fcode = this.hsize; fcode < 65536; fcode *= 2) - { - ++hshift; - } - - hshift = 8 - hshift; // set hash code range bound - - hsizeReg = this.hsize; - - this.ResetCodeTable(); // clear hash table - + this.ResetCodeTable(); // Clear hash table this.Output(this.clearCode, stream); ref int hashTableRef = ref MemoryMarshal.GetReference(this.hashTable.GetSpan()); ref int codeTableRef = ref MemoryMarshal.GetReference(this.codeTable.GetSpan()); - while (this.position < indexedPixels.Length) - { - c = this.NextPixel(indexedPixels); + int entry = indexedPixels[0, 0]; - fcode = (c << MaxBits) + ent; - int i = (c << hshift) ^ ent /* = 0 */; + for (int y = 0; y < indexedPixels.Height; y++) + { + ref byte rowSpanRef = ref MemoryMarshal.GetReference(indexedPixels.GetRowSpan(y)); + int offsetX = y == 0 ? 1 : 0; - if (Unsafe.Add(ref hashTableRef, i) == fcode) + for (int x = offsetX; x < indexedPixels.Width; x++) { - ent = Unsafe.Add(ref codeTableRef, i); - continue; - } + int code = Unsafe.Add(ref rowSpanRef, x); + int freeCode = (code << MaxBits) + entry; + int hashIndex = (code << HashShift) ^ entry; - // Non-empty slot - if (Unsafe.Add(ref hashTableRef, i) >= 0) - { - int disp = 1; - if (i != 0) + if (Unsafe.Add(ref hashTableRef, hashIndex) == freeCode) { - disp = hsizeReg - i; + entry = Unsafe.Add(ref codeTableRef, hashIndex); + continue; } - do + // Non-empty slot + if (Unsafe.Add(ref hashTableRef, hashIndex) >= 0) { - if ((i -= disp) < 0) + int disp = 1; + if (hashIndex != 0) + { + disp = HashSize - hashIndex; + } + + do { - i += hsizeReg; + if ((hashIndex -= disp) < 0) + { + hashIndex += HashSize; + } + + if (Unsafe.Add(ref hashTableRef, hashIndex) == freeCode) + { + entry = Unsafe.Add(ref codeTableRef, hashIndex); + break; + } } + while (Unsafe.Add(ref hashTableRef, hashIndex) >= 0); - if (Unsafe.Add(ref hashTableRef, i) == fcode) + if (Unsafe.Add(ref hashTableRef, hashIndex) == freeCode) { - ent = Unsafe.Add(ref codeTableRef, i); - break; + continue; } } - while (Unsafe.Add(ref hashTableRef, i) >= 0); - if (Unsafe.Add(ref hashTableRef, i) == fcode) + this.Output(entry, stream); + entry = code; + if (this.freeEntry < MaxMaxCode) { - continue; + Unsafe.Add(ref codeTableRef, hashIndex) = this.freeEntry++; // code -> hashtable + Unsafe.Add(ref hashTableRef, hashIndex) = freeCode; + } + else + { + this.ClearBlock(stream); } - } - - this.Output(ent, stream); - ent = c; - if (this.freeEntry < MaxMaxCode) - { - Unsafe.Add(ref codeTableRef, i) = this.freeEntry++; // code -> hashtable - Unsafe.Add(ref hashTableRef, i) = fcode; - } - else - { - this.ClearBlock(stream); } } - // Put out the final code. - this.Output(ent, stream); - + // Output the final code. + this.Output(entry, stream); this.Output(this.eofCode, stream); } @@ -366,19 +352,6 @@ namespace SixLabors.ImageSharp.Formats.Gif this.accumulatorCount = 0; } - /// - /// Reads the next pixel from the image. - /// - /// The span of indexed pixels. - /// - /// The - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private int NextPixel(ReadOnlySpan indexedPixels) - { - return indexedPixels[this.position++] & 0xFF; - } - /// /// Output the current code to the stream. /// diff --git a/src/ImageSharp/Processing/Processors/Quantization/IndexedImageFrame{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/IndexedImageFrame{TPixel}.cs index ac737f452..c271ce742 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/IndexedImageFrame{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/IndexedImageFrame{TPixel}.cs @@ -4,6 +4,7 @@ using System; using System.Buffers; using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -13,10 +14,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// A pixel-specific image frame where each pixel buffer value represents an index in a color palette. /// /// The pixel format. - public sealed class IndexedImageFrame : IDisposable + public sealed class IndexedImageFrame : IPixelSource, IDisposable where TPixel : unmanaged, IPixel { - private IMemoryOwner pixelsOwner; + private Buffer2D pixelBuffer; private IMemoryOwner paletteOwner; private bool isDisposed; @@ -39,7 +40,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization this.Configuration = configuration; this.Width = width; this.Height = height; - this.pixelsOwner = configuration.MemoryAllocator.AllocateManagedByteBuffer(width * height); + this.pixelBuffer = configuration.MemoryAllocator.Allocate2D(width, height); // Copy the palette over. We want the lifetime of this frame to be independant of any palette source. this.paletteOwner = configuration.MemoryAllocator.Allocate(palette.Length); @@ -67,16 +68,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// public ReadOnlyMemory Palette { get; } - /// - /// Gets the pixels of this . - /// - /// The - [MethodImpl(InliningOptions.ShortMethod)] - public ReadOnlySpan GetPixelBufferSpan() => this.pixelsOwner.GetSpan(); // TODO: Buffer2D + /// + Buffer2D IPixelSource.PixelBuffer => this.pixelBuffer; /// /// 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 index in the pixel buffer. /// The pixel row as a . @@ -87,7 +84,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. /// /// /// Note: Values written to this span are not sanitized against the palette length. @@ -98,7 +95,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// The pixel row as a . [MethodImpl(InliningOptions.ShortMethod)] public Span GetWritablePixelRowSpanUnsafe(int rowIndex) - => this.pixelsOwner.GetSpan().Slice(rowIndex * this.Width, this.Width); + => this.pixelBuffer.GetRowSpan(rowIndex); /// public void Dispose() @@ -106,9 +103,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization if (!this.isDisposed) { this.isDisposed = true; - this.pixelsOwner.Dispose(); + this.pixelBuffer.Dispose(); this.paletteOwner.Dispose(); - this.pixelsOwner = null; + this.pixelBuffer = null; this.paletteOwner = null; } } diff --git a/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs index 4583b7cff..386caf1be 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs @@ -68,21 +68,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization [MethodImpl(InliningOptions.ShortMethod)] public void Invoke(in RowInterval rows) { - ReadOnlySpan quantizedPixelSpan = this.quantized.GetPixelBufferSpan(); ReadOnlySpan paletteSpan = this.quantized.Palette.Span; int offsetY = this.bounds.Top; int offsetX = this.bounds.Left; - int width = this.bounds.Width; for (int y = rows.Min; y < rows.Max; y++) { Span row = this.source.GetPixelRowSpan(y); - int rowStart = (y - offsetY) * width; + ReadOnlySpan quantizedRow = this.quantized.GetPixelRowSpan(y - offsetY); for (int x = this.bounds.Left; x < this.bounds.Right; x++) { - int i = rowStart + x - offsetX; - row[x] = paletteSpan[quantizedPixelSpan[i]]; + row[x] = paletteSpan[quantizedRow[x - offsetX]]; } } } diff --git a/tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs b/tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs index 7e4eced8f..7945741b0 100644 --- a/tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs +++ b/tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs @@ -74,7 +74,7 @@ namespace SixLabors.ImageSharp.Tests using (IndexedImageFrame quantized = frameQuantizer.QuantizeFrame(frame, frame.Bounds())) { int index = this.GetTransparentIndex(quantized); - Assert.Equal(index, quantized.GetPixelBufferSpan()[0]); + Assert.Equal(index, quantized.GetPixelRowSpan(0)[0]); } } } @@ -104,7 +104,7 @@ namespace SixLabors.ImageSharp.Tests using (IndexedImageFrame quantized = frameQuantizer.QuantizeFrame(frame, frame.Bounds())) { int index = this.GetTransparentIndex(quantized); - Assert.Equal(index, quantized.GetPixelBufferSpan()[0]); + Assert.Equal(index, quantized.GetPixelRowSpan(0)[0]); } } } diff --git a/tests/ImageSharp.Tests/Quantization/WuQuantizerTests.cs b/tests/ImageSharp.Tests/Quantization/WuQuantizerTests.cs index 2a0a02d94..37b8cab60 100644 --- a/tests/ImageSharp.Tests/Quantization/WuQuantizerTests.cs +++ b/tests/ImageSharp.Tests/Quantization/WuQuantizerTests.cs @@ -24,10 +24,11 @@ namespace SixLabors.ImageSharp.Tests.Quantization using IndexedImageFrame result = frameQuantizer.QuantizeFrame(frame, frame.Bounds()); Assert.Equal(1, result.Palette.Length); - Assert.Equal(1, result.GetPixelBufferSpan().Length); + Assert.Equal(1, result.Width); + Assert.Equal(1, result.Height); Assert.Equal(Color.Black, (Color)result.Palette.Span[0]); - Assert.Equal(0, result.GetPixelBufferSpan()[0]); + Assert.Equal(0, result.GetPixelRowSpan(0)[0]); } [Fact] @@ -43,10 +44,11 @@ namespace SixLabors.ImageSharp.Tests.Quantization using IndexedImageFrame result = frameQuantizer.QuantizeFrame(frame, frame.Bounds()); Assert.Equal(1, result.Palette.Length); - Assert.Equal(1, result.GetPixelBufferSpan().Length); + Assert.Equal(1, result.Width); + Assert.Equal(1, result.Height); Assert.Equal(default, result.Palette.Span[0]); - Assert.Equal(0, result.GetPixelBufferSpan()[0]); + Assert.Equal(0, result.GetPixelRowSpan(0)[0]); } [Fact] @@ -88,7 +90,8 @@ namespace SixLabors.ImageSharp.Tests.Quantization using IndexedImageFrame result = frameQuantizer.QuantizeFrame(frame, frame.Bounds()); Assert.Equal(256, result.Palette.Length); - Assert.Equal(256, result.GetPixelBufferSpan().Length); + Assert.Equal(1, result.Width); + Assert.Equal(256, result.Height); var actualImage = new Image(1, 256); @@ -97,17 +100,18 @@ namespace SixLabors.ImageSharp.Tests.Quantization for (int y = 0; y < actualImage.Height; y++) { Span row = actualImage.GetPixelRowSpan(y); - ReadOnlySpan quantizedPixelSpan = result.GetPixelBufferSpan(); - int yy = y * actualImage.Width; + ReadOnlySpan quantizedPixelSpan = result.GetPixelRowSpan(y); for (int x = 0; x < actualImage.Width; x++) { - int i = x + yy; - row[x] = paletteSpan[Math.Min(paletteCount, quantizedPixelSpan[i])]; + row[x] = paletteSpan[Math.Min(paletteCount, quantizedPixelSpan[x])]; } } - Assert.True(image.GetPixelSpan().SequenceEqual(actualImage.GetPixelSpan())); + for (int y = 0; y < image.Height; y++) + { + Assert.True(image.GetPixelRowSpan(y).SequenceEqual(actualImage.GetPixelRowSpan(y))); + } } [Theory] @@ -155,25 +159,27 @@ namespace SixLabors.ImageSharp.Tests.Quantization using (IndexedImageFrame result = frameQuantizer.QuantizeFrame(frame, frame.Bounds())) { Assert.Equal(4 * 8, result.Palette.Length); - Assert.Equal(256, result.GetPixelBufferSpan().Length); + Assert.Equal(1, result.Width); + Assert.Equal(256, result.Height); ReadOnlySpan paletteSpan = result.Palette.Span; int paletteCount = paletteSpan.Length - 1; for (int y = 0; y < actualImage.Height; y++) { Span row = actualImage.GetPixelRowSpan(y); - ReadOnlySpan quantizedPixelSpan = result.GetPixelBufferSpan(); - int yy = y * actualImage.Width; + ReadOnlySpan quantizedPixelSpan = result.GetPixelRowSpan(y); for (int x = 0; x < actualImage.Width; x++) { - int i = x + yy; - row[x] = paletteSpan[Math.Min(paletteCount, quantizedPixelSpan[i])]; + row[x] = paletteSpan[Math.Min(paletteCount, quantizedPixelSpan[x])]; } } } - Assert.True(expectedImage.GetPixelSpan().SequenceEqual(actualImage.GetPixelSpan())); + for (int y = 0; y < expectedImage.Height; y++) + { + Assert.True(expectedImage.GetPixelRowSpan(y).SequenceEqual(actualImage.GetPixelRowSpan(y))); + } } } } From 11e62696e70a7e5b016034e4fbb33d59d0681013 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sat, 28 Mar 2020 11:51:55 +0100 Subject: [PATCH 33/58] Use == instead of is --- src/ImageSharp/Formats/Tga/TgaDecoderCore.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs index bba04f98a..cca34c9b0 100644 --- a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs +++ b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs @@ -97,7 +97,7 @@ namespace SixLabors.ImageSharp.Formats.Tga TgaThrowHelper.ThrowNotSupportedException($"Unknown tga colormap type {this.fileHeader.ColorMapType} found"); } - if (this.fileHeader.Width is 0 || this.fileHeader.Height is 0) + if (this.fileHeader.Width == 0 || this.fileHeader.Height == 0) { throw new UnknownImageFormatException("Width or height cannot be 0"); } @@ -105,7 +105,7 @@ namespace SixLabors.ImageSharp.Formats.Tga var image = Image.CreateUninitialized(this.configuration, this.fileHeader.Width, this.fileHeader.Height, this.metadata); Buffer2D pixels = image.GetRootFramePixelBuffer(); - if (this.fileHeader.ColorMapType is 1) + if (this.fileHeader.ColorMapType == 1) { if (this.fileHeader.CMapLength <= 0) { @@ -123,7 +123,7 @@ namespace SixLabors.ImageSharp.Formats.Tga { this.currentStream.Read(palette.Array, this.fileHeader.CMapStart, colorMapSizeInBytes); - if (this.fileHeader.ImageType is TgaImageType.RleColorMapped) + if (this.fileHeader.ImageType == TgaImageType.RleColorMapped) { this.ReadPalettedRle( this.fileHeader.Width, @@ -341,7 +341,7 @@ namespace SixLabors.ImageSharp.Formats.Tga } else { - var alpha = alphaBits is 0 ? byte.MaxValue : bufferSpan[idx + 3]; + var alpha = alphaBits == 0 ? byte.MaxValue : bufferSpan[idx + 3]; color.FromBgra32(new Bgra32(bufferSpan[idx + 2], bufferSpan[idx + 1], bufferSpan[idx], (byte)alpha)); } @@ -445,7 +445,7 @@ namespace SixLabors.ImageSharp.Formats.Tga private void ReadBgra32(int width, int height, Buffer2D pixels, bool inverted) where TPixel : unmanaged, IPixel { - if (this.tgaMetadata.AlphaChannelBits is 8) + if (this.tgaMetadata.AlphaChannelBits == 8) { using (IManagedByteBuffer row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 4, 0)) { @@ -476,7 +476,7 @@ namespace SixLabors.ImageSharp.Formats.Tga for (int x = 0; x < width; x++) { int idx = x * 4; - var alpha = alphaBits is 0 ? byte.MaxValue : rowSpan[idx + 3]; + var alpha = alphaBits == 0 ? byte.MaxValue : rowSpan[idx + 3]; color.FromBgra32(new Bgra32(rowSpan[idx + 2], rowSpan[idx + 1], rowSpan[idx], (byte)alpha)); pixelRow[x] = color; } @@ -534,7 +534,7 @@ namespace SixLabors.ImageSharp.Formats.Tga } else { - var alpha = alphaBits is 0 ? byte.MaxValue : bufferSpan[idx + 3]; + var alpha = alphaBits == 0 ? byte.MaxValue : bufferSpan[idx + 3]; color.FromBgra32(new Bgra32(bufferSpan[idx + 2], bufferSpan[idx + 1], bufferSpan[idx], (byte)alpha)); } @@ -579,7 +579,7 @@ namespace SixLabors.ImageSharp.Formats.Tga // The high bit of a run length packet is set to 1. int highBit = runLengthByte >> 7; - if (highBit is 1) + if (highBit == 1) { int runLength = runLengthByte & 127; this.currentStream.Read(pixel, 0, bytesPerPixel); From 13f411d0601ece2fd943fe29bd6e0326fac7cf6f Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 28 Mar 2020 16:23:29 +0000 Subject: [PATCH 34/58] Move IndexedImageFrame to root namespace. --- .../Processors/Quantization => }/IndexedImageFrame{TPixel}.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) rename src/ImageSharp/{Processing/Processors/Quantization => }/IndexedImageFrame{TPixel}.cs (98%) diff --git a/src/ImageSharp/Processing/Processors/Quantization/IndexedImageFrame{TPixel}.cs b/src/ImageSharp/IndexedImageFrame{TPixel}.cs similarity index 98% rename from src/ImageSharp/Processing/Processors/Quantization/IndexedImageFrame{TPixel}.cs rename to src/ImageSharp/IndexedImageFrame{TPixel}.cs index c271ce742..07451029e 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/IndexedImageFrame{TPixel}.cs +++ b/src/ImageSharp/IndexedImageFrame{TPixel}.cs @@ -7,8 +7,9 @@ using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors.Quantization; -namespace SixLabors.ImageSharp.Processing.Processors.Quantization +namespace SixLabors.ImageSharp { /// /// A pixel-specific image frame where each pixel buffer value represents an index in a color palette. From ae1480c0c2dda2ccd62a2fe190bd2bb2da875d3c Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sat, 28 Mar 2020 20:46:05 +0100 Subject: [PATCH 35/58] Add support for top right and bottom right image origin --- src/ImageSharp/Formats/Tga/TgaDecoderCore.cs | 239 ++++++++++++++----- src/ImageSharp/Formats/Tga/TgaImageOrigin.cs | 28 +++ 2 files changed, 205 insertions(+), 62 deletions(-) create mode 100644 src/ImageSharp/Formats/Tga/TgaImageOrigin.cs diff --git a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs index cca34c9b0..270dfb22b 100644 --- a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs +++ b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs @@ -17,6 +17,11 @@ namespace SixLabors.ImageSharp.Formats.Tga /// internal sealed class TgaDecoderCore { + /// + /// A scratch buffer to reduce allocations. + /// + private readonly byte[] buffer = new byte[4]; + /// /// The metadata. /// @@ -88,7 +93,7 @@ namespace SixLabors.ImageSharp.Formats.Tga { try { - bool inverted = this.ReadFileHeader(stream); + TgaImageOrigin origin = this.ReadFileHeader(stream); this.currentStream.Skip(this.fileHeader.IdLength); // Parse the color map, if present. @@ -131,7 +136,7 @@ namespace SixLabors.ImageSharp.Formats.Tga pixels, palette.Array, colorMapPixelSizeInBytes, - inverted); + origin); } else { @@ -141,7 +146,7 @@ namespace SixLabors.ImageSharp.Formats.Tga pixels, palette.Array, colorMapPixelSizeInBytes, - inverted); + origin); } } @@ -160,11 +165,11 @@ namespace SixLabors.ImageSharp.Formats.Tga case 8: if (this.fileHeader.ImageType.IsRunLengthEncoded()) { - this.ReadRle(this.fileHeader.Width, this.fileHeader.Height, pixels, 1, inverted); + this.ReadRle(this.fileHeader.Width, this.fileHeader.Height, pixels, 1, origin); } else { - this.ReadMonoChrome(this.fileHeader.Width, this.fileHeader.Height, pixels, inverted); + this.ReadMonoChrome(this.fileHeader.Width, this.fileHeader.Height, pixels, origin); } break; @@ -173,11 +178,11 @@ namespace SixLabors.ImageSharp.Formats.Tga case 16: if (this.fileHeader.ImageType.IsRunLengthEncoded()) { - this.ReadRle(this.fileHeader.Width, this.fileHeader.Height, pixels, 2, inverted); + this.ReadRle(this.fileHeader.Width, this.fileHeader.Height, pixels, 2, origin); } else { - this.ReadBgra16(this.fileHeader.Width, this.fileHeader.Height, pixels, inverted); + this.ReadBgra16(this.fileHeader.Width, this.fileHeader.Height, pixels, origin); } break; @@ -185,11 +190,11 @@ namespace SixLabors.ImageSharp.Formats.Tga case 24: if (this.fileHeader.ImageType.IsRunLengthEncoded()) { - this.ReadRle(this.fileHeader.Width, this.fileHeader.Height, pixels, 3, inverted); + this.ReadRle(this.fileHeader.Width, this.fileHeader.Height, pixels, 3, origin); } else { - this.ReadBgr24(this.fileHeader.Width, this.fileHeader.Height, pixels, inverted); + this.ReadBgr24(this.fileHeader.Width, this.fileHeader.Height, pixels, origin); } break; @@ -197,11 +202,11 @@ namespace SixLabors.ImageSharp.Formats.Tga case 32: if (this.fileHeader.ImageType.IsRunLengthEncoded()) { - this.ReadRle(this.fileHeader.Width, this.fileHeader.Height, pixels, 4, inverted); + this.ReadRle(this.fileHeader.Width, this.fileHeader.Height, pixels, 4, origin); } else { - this.ReadBgra32(this.fileHeader.Width, this.fileHeader.Height, pixels, inverted); + this.ReadBgra32(this.fileHeader.Width, this.fileHeader.Height, pixels, origin); } break; @@ -228,8 +233,8 @@ namespace SixLabors.ImageSharp.Formats.Tga /// The to assign the palette to. /// The color palette. /// Color map size of one entry in bytes. - /// Indicates, if the origin of the image is top left rather the bottom left (the default). - private void ReadPaletted(int width, int height, Buffer2D pixels, byte[] palette, int colorMapPixelSizeInBytes, bool inverted) + /// The image origin. + private void ReadPaletted(int width, int height, Buffer2D pixels, byte[] palette, int colorMapPixelSizeInBytes, TgaImageOrigin origin) where TPixel : unmanaged, IPixel { using (IManagedByteBuffer row = this.memoryAllocator.AllocateManagedByteBuffer(width, AllocationOptions.Clean)) @@ -240,7 +245,7 @@ namespace SixLabors.ImageSharp.Formats.Tga for (int y = 0; y < height; y++) { this.currentStream.Read(row); - int newY = Invert(y, height, inverted); + int newY = InvertY(y, height, origin); Span pixelRow = pixels.GetRowSpan(newY); switch (colorMapPixelSizeInBytes) { @@ -257,7 +262,8 @@ namespace SixLabors.ImageSharp.Formats.Tga } color.FromBgra5551(bgra); - pixelRow[x] = color; + int newX = InvertX(x, width, origin); + pixelRow[newX] = color; } break; @@ -267,7 +273,8 @@ namespace SixLabors.ImageSharp.Formats.Tga { int colorIndex = rowSpan[x]; color.FromBgr24(Unsafe.As(ref palette[colorIndex * colorMapPixelSizeInBytes])); - pixelRow[x] = color; + int newX = InvertX(x, width, origin); + pixelRow[newX] = color; } break; @@ -277,7 +284,8 @@ namespace SixLabors.ImageSharp.Formats.Tga { int colorIndex = rowSpan[x]; color.FromBgra32(Unsafe.As(ref palette[colorIndex * colorMapPixelSizeInBytes])); - pixelRow[x] = color; + int newX = InvertX(x, width, origin); + pixelRow[newX] = color; } break; @@ -295,8 +303,8 @@ namespace SixLabors.ImageSharp.Formats.Tga /// The to assign the palette to. /// The color palette. /// Color map size of one entry in bytes. - /// Indicates, if the origin of the image is top left rather the bottom left (the default). - private void ReadPalettedRle(int width, int height, Buffer2D pixels, byte[] palette, int colorMapPixelSizeInBytes, bool inverted) + /// The image origin. + private void ReadPalettedRle(int width, int height, Buffer2D pixels, byte[] palette, int colorMapPixelSizeInBytes, TgaImageOrigin origin) where TPixel : unmanaged, IPixel { int bytesPerPixel = 1; @@ -309,7 +317,7 @@ namespace SixLabors.ImageSharp.Formats.Tga for (int y = 0; y < height; y++) { - int newY = Invert(y, height, inverted); + int newY = InvertY(y, height, origin); Span pixelRow = pixels.GetRowSpan(newY); int rowStartIdx = y * width * bytesPerPixel; for (int x = 0; x < width; x++) @@ -348,7 +356,8 @@ namespace SixLabors.ImageSharp.Formats.Tga break; } - pixelRow[x] = color; + int newX = InvertX(x, width, origin); + pixelRow[newX] = color; } } } @@ -361,16 +370,36 @@ namespace SixLabors.ImageSharp.Formats.Tga /// The width of the image. /// The height of the image. /// The to assign the palette to. - /// Indicates, if the origin of the image is top left rather the bottom left (the default). - private void ReadMonoChrome(int width, int height, Buffer2D pixels, bool inverted) + /// the image origin. + private void ReadMonoChrome(int width, int height, Buffer2D pixels, TgaImageOrigin origin) where TPixel : unmanaged, IPixel { + bool isXInverted = origin == TgaImageOrigin.BottomRight || origin == TgaImageOrigin.TopRight; + if (isXInverted) + { + TPixel color = default; + for (int y = 0; y < height; y++) + { + int newY = InvertY(y, height, origin); + Span pixelSpan = pixels.GetRowSpan(newY); + for (int x = 0; x < width; x++) + { + var pixelValue = (byte)this.currentStream.ReadByte(); + color.FromL8(Unsafe.As(ref pixelValue)); + int newX = InvertX(x, width, origin); + pixelSpan[newX] = color; + } + } + + return; + } + using (IManagedByteBuffer row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 1, 0)) { for (int y = 0; y < height; y++) { this.currentStream.Read(row); - int newY = Invert(y, height, inverted); + int newY = InvertY(y, height, origin); Span pixelSpan = pixels.GetRowSpan(newY); PixelOperations.Instance.FromL8Bytes(this.configuration, row.GetSpan(), pixelSpan, width); } @@ -384,29 +413,50 @@ namespace SixLabors.ImageSharp.Formats.Tga /// The width of the image. /// The height of the image. /// The to assign the palette to. - /// Indicates, if the origin of the image is top left rather the bottom left (the default). - private void ReadBgra16(int width, int height, Buffer2D pixels, bool inverted) + /// The image origin. + private void ReadBgra16(int width, int height, Buffer2D pixels, TgaImageOrigin origin) where TPixel : unmanaged, IPixel { + TPixel color = default; + bool isXInverted = origin == TgaImageOrigin.BottomRight || origin == TgaImageOrigin.TopRight; using (IManagedByteBuffer row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 2, 0)) { for (int y = 0; y < height; y++) { - this.currentStream.Read(row); - Span rowSpan = row.GetSpan(); + int newY = InvertY(y, height, origin); + Span pixelSpan = pixels.GetRowSpan(newY); - if (!this.hasAlpha) + if (isXInverted) { - // We need to set the alpha component value to fully opaque. - for (int x = 1; x < rowSpan.Length; x += 2) + for (int x = 0; x < width; x++) { - rowSpan[x] = (byte)(rowSpan[x] | (1 << 7)); + this.currentStream.Read(this.buffer, 0, 2); + if (!this.hasAlpha) + { + this.buffer[1] |= 1 << 7; + } + + color.FromBgra5551(Unsafe.As(ref this.buffer[0])); + int newX = InvertX(x, width, origin); + pixelSpan[newX] = color; } } + else + { + this.currentStream.Read(row); + Span rowSpan = row.GetSpan(); - int newY = Invert(y, height, inverted); - Span pixelSpan = pixels.GetRowSpan(newY); - PixelOperations.Instance.FromBgra5551Bytes(this.configuration, rowSpan, pixelSpan, width); + if (!this.hasAlpha) + { + // We need to set the alpha component value to fully opaque. + for (int x = 1; x < rowSpan.Length; x += 2) + { + rowSpan[x] |= (1 << 7); + } + } + + PixelOperations.Instance.FromBgra5551Bytes(this.configuration, rowSpan, pixelSpan, width); + } } } } @@ -418,16 +468,36 @@ namespace SixLabors.ImageSharp.Formats.Tga /// The width of the image. /// The height of the image. /// The to assign the palette to. - /// Indicates, if the origin of the image is top left rather the bottom left (the default). - private void ReadBgr24(int width, int height, Buffer2D pixels, bool inverted) + /// The image origin. + private void ReadBgr24(int width, int height, Buffer2D pixels, TgaImageOrigin origin) where TPixel : unmanaged, IPixel { + bool isXInverted = origin == TgaImageOrigin.BottomRight || origin == TgaImageOrigin.TopRight; + if (isXInverted) + { + TPixel color = default; + for (int y = 0; y < height; y++) + { + int newY = InvertY(y, height, origin); + Span pixelSpan = pixels.GetRowSpan(newY); + for (int x = 0; x < width; x++) + { + this.currentStream.Read(this.buffer, 0, 3); + color.FromBgr24(Unsafe.As(ref this.buffer[0])); + int newX = InvertX(x, width, origin); + pixelSpan[newX] = color; + } + } + + return; + } + using (IManagedByteBuffer row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 3, 0)) { for (int y = 0; y < height; y++) { this.currentStream.Read(row); - int newY = Invert(y, height, inverted); + int newY = InvertY(y, height, origin); Span pixelSpan = pixels.GetRowSpan(newY); PixelOperations.Instance.FromBgr24Bytes(this.configuration, row.GetSpan(), pixelSpan, width); } @@ -441,18 +511,38 @@ namespace SixLabors.ImageSharp.Formats.Tga /// The width of the image. /// The height of the image. /// The to assign the palette to. - /// Indicates, if the origin of the image is top left rather the bottom left (the default). - private void ReadBgra32(int width, int height, Buffer2D pixels, bool inverted) + /// The image origin. + private void ReadBgra32(int width, int height, Buffer2D pixels, TgaImageOrigin origin) where TPixel : unmanaged, IPixel { + TPixel color = default; if (this.tgaMetadata.AlphaChannelBits == 8) { + bool isXInverted = origin == TgaImageOrigin.BottomRight || origin == TgaImageOrigin.TopRight; + if (isXInverted) + { + for (int y = 0; y < height; y++) + { + int newY = InvertY(y, height, origin); + Span pixelSpan = pixels.GetRowSpan(newY); + for (int x = 0; x < width; x++) + { + this.currentStream.Read(this.buffer, 0, 4); + color.FromBgra32(Unsafe.As(ref this.buffer[0])); + int newX = InvertX(x, width, origin); + pixelSpan[newX] = color; + } + } + + return; + } + using (IManagedByteBuffer row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 4, 0)) { for (int y = 0; y < height; y++) { this.currentStream.Read(row); - int newY = Invert(y, height, inverted); + int newY = InvertY(y, height, origin); Span pixelSpan = pixels.GetRowSpan(newY); PixelOperations.Instance.FromBgra32Bytes(this.configuration, row.GetSpan(), pixelSpan, width); @@ -462,14 +552,13 @@ namespace SixLabors.ImageSharp.Formats.Tga return; } - TPixel color = default; var alphaBits = this.tgaMetadata.AlphaChannelBits; using (IManagedByteBuffer row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 4, 0)) { for (int y = 0; y < height; y++) { this.currentStream.Read(row); - int newY = Invert(y, height, inverted); + int newY = InvertY(y, height, origin); Span pixelRow = pixels.GetRowSpan(newY); Span rowSpan = row.GetSpan(); @@ -478,7 +567,8 @@ namespace SixLabors.ImageSharp.Formats.Tga int idx = x * 4; var alpha = alphaBits == 0 ? byte.MaxValue : rowSpan[idx + 3]; color.FromBgra32(new Bgra32(rowSpan[idx + 2], rowSpan[idx + 1], rowSpan[idx], (byte)alpha)); - pixelRow[x] = color; + int newX = InvertX(x, width, origin); + pixelRow[newX] = color; } } } @@ -492,8 +582,8 @@ namespace SixLabors.ImageSharp.Formats.Tga /// The height of the image. /// The to assign the palette to. /// The bytes per pixel. - /// Indicates, if the origin of the image is top left rather the bottom left (the default). - private void ReadRle(int width, int height, Buffer2D pixels, int bytesPerPixel, bool inverted) + /// The image origin. + private void ReadRle(int width, int height, Buffer2D pixels, int bytesPerPixel, TgaImageOrigin origin) where TPixel : unmanaged, IPixel { TPixel color = default; @@ -504,7 +594,7 @@ namespace SixLabors.ImageSharp.Formats.Tga this.UncompressRle(width, height, bufferSpan, bytesPerPixel); for (int y = 0; y < height; y++) { - int newY = Invert(y, height, inverted); + int newY = InvertY(y, height, origin); Span pixelRow = pixels.GetRowSpan(newY); int rowStartIdx = y * width * bytesPerPixel; for (int x = 0; x < width; x++) @@ -541,7 +631,8 @@ namespace SixLabors.ImageSharp.Formats.Tga break; } - pixelRow[x] = color; + int newX = InvertX(x, width, origin); + pixelRow[newX] = color; } } } @@ -609,18 +700,48 @@ namespace SixLabors.ImageSharp.Formats.Tga /// Returns the y- value based on the given height. /// /// The y- value representing the current row. - /// The height of the bitmap. - /// Whether the bitmap is inverted. + /// The height of the image. + /// The image origin. + /// The representing the inverted value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int InvertY(int y, int height, TgaImageOrigin origin) + { + switch (origin) + { + case TgaImageOrigin.BottomLeft: + case TgaImageOrigin.BottomRight: + return height - y - 1; + default: + return y; + } + } + + /// + /// Returns the x- value based on the given width. + /// + /// The x- value representing the current column. + /// The width of the image. + /// The image origin. /// The representing the inverted value. [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int Invert(int y, int height, bool inverted) => (!inverted) ? height - y - 1 : y; + private static int InvertX(int x, int width, TgaImageOrigin origin) + { + switch (origin) + { + case TgaImageOrigin.TopRight: + case TgaImageOrigin.BottomRight: + return width - x - 1; + default: + return x; + } + } /// /// Reads the tga file header from the stream. /// /// The containing image data. - /// true, if the image origin is top left. - private bool ReadFileHeader(Stream stream) + /// The image origin. + private TgaImageOrigin ReadFileHeader(Stream stream) { this.currentStream = stream; @@ -641,15 +762,9 @@ namespace SixLabors.ImageSharp.Formats.Tga this.tgaMetadata.AlphaChannelBits = (byte)alphaBits; this.hasAlpha = alphaBits > 0; - // TODO: bits 4 and 5 describe the image origin. See spec page 9. bit 4 is currently ignored. - // Theoretically the origin could also be top right and bottom right. - // Bit at position 5 of the descriptor indicates, that the origin is top left instead of bottom left. - if ((this.fileHeader.ImageDescriptor & (1 << 5)) != 0) - { - return true; - } - - return false; + // Bits 4 and 5 describe the image origin. + var origin = (TgaImageOrigin)((this.fileHeader.ImageDescriptor & 0x30) >> 4); + return origin; } } } diff --git a/src/ImageSharp/Formats/Tga/TgaImageOrigin.cs b/src/ImageSharp/Formats/Tga/TgaImageOrigin.cs new file mode 100644 index 000000000..06d7b5945 --- /dev/null +++ b/src/ImageSharp/Formats/Tga/TgaImageOrigin.cs @@ -0,0 +1,28 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats.Tga +{ + internal enum TgaImageOrigin + { + /// + /// Bottom left origin. + /// + BottomLeft = 0, + + /// + /// Bottom right origin. + /// + BottomRight = 1, + + /// + /// Top left origin. + /// + TopLeft = 2, + + /// + /// Top right origin. + /// + TopRight = 3, + } +} From 74e1106f29cda6f1a13a73721fcf87231a9ab6f3 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 29 Mar 2020 10:12:31 +0200 Subject: [PATCH 36/58] Add support for decoding 16bit monochrome images --- src/ImageSharp/Formats/Tga/TgaDecoderCore.cs | 31 +++++++++++++++++--- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs index 270dfb22b..dd3b6a821 100644 --- a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs +++ b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs @@ -436,7 +436,15 @@ namespace SixLabors.ImageSharp.Formats.Tga this.buffer[1] |= 1 << 7; } - color.FromBgra5551(Unsafe.As(ref this.buffer[0])); + if (this.fileHeader.ImageType == TgaImageType.BlackAndWhite) + { + color.FromLa16(Unsafe.As(ref this.buffer[0])); + } + else + { + color.FromBgra5551(Unsafe.As(ref this.buffer[0])); + } + int newX = InvertX(x, width, origin); pixelSpan[newX] = color; } @@ -451,11 +459,18 @@ namespace SixLabors.ImageSharp.Formats.Tga // We need to set the alpha component value to fully opaque. for (int x = 1; x < rowSpan.Length; x += 2) { - rowSpan[x] |= (1 << 7); + rowSpan[x] |= 1 << 7; } } - PixelOperations.Instance.FromBgra5551Bytes(this.configuration, rowSpan, pixelSpan, width); + if (this.fileHeader.ImageType == TgaImageType.BlackAndWhite) + { + PixelOperations.Instance.FromLa16Bytes(this.configuration, rowSpan, pixelSpan, width); + } + else + { + PixelOperations.Instance.FromBgra5551Bytes(this.configuration, rowSpan, pixelSpan, width); + } } } } @@ -612,7 +627,15 @@ namespace SixLabors.ImageSharp.Formats.Tga bufferSpan[idx + 1] = (byte)(bufferSpan[idx + 1] | 128); } - color.FromBgra5551(Unsafe.As(ref bufferSpan[idx])); + if (this.fileHeader.ImageType == TgaImageType.RleBlackAndWhite) + { + color.FromLa16(Unsafe.As(ref bufferSpan[idx])); + } + else + { + color.FromBgra5551(Unsafe.As(ref bufferSpan[idx])); + } + break; case 3: color.FromBgr24(Unsafe.As(ref bufferSpan[idx])); From 4561af046c98a824b03d675e823141f480b3d498 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 29 Mar 2020 10:13:20 +0200 Subject: [PATCH 37/58] Add additional tests for monochrome images --- .../Formats/Tga/TgaDecoderTests.cs | 112 +++++++++++++++--- .../Formats/Tga/TgaEncoderTests.cs | 10 +- tests/ImageSharp.Tests/TestImages.cs | 11 +- tests/Images/Input/Tga/grayscale_a_LL.tga | 3 + tests/Images/Input/Tga/grayscale_a_LR.tga | 3 + tests/Images/Input/Tga/grayscale_a_UL.tga | 3 + tests/Images/Input/Tga/grayscale_a_rle_LL.tga | 3 + tests/Images/Input/Tga/grayscale_a_rle_LR.tga | 3 + tests/Images/Input/Tga/grayscale_a_rle_UL.tga | 3 + tests/Images/Input/Tga/grayscale_a_rle_UR.tga | 3 + 10 files changed, 133 insertions(+), 21 deletions(-) create mode 100644 tests/Images/Input/Tga/grayscale_a_LL.tga create mode 100644 tests/Images/Input/Tga/grayscale_a_LR.tga create mode 100644 tests/Images/Input/Tga/grayscale_a_UL.tga create mode 100644 tests/Images/Input/Tga/grayscale_a_rle_LL.tga create mode 100644 tests/Images/Input/Tga/grayscale_a_rle_LR.tga create mode 100644 tests/Images/Input/Tga/grayscale_a_rle_UL.tga create mode 100644 tests/Images/Input/Tga/grayscale_a_rle_UR.tga diff --git a/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs index 767b3b954..2ce6eb3c2 100644 --- a/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs @@ -20,8 +20,104 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga private static TgaDecoder TgaDecoder => new TgaDecoder(); [Theory] - [WithFile(Grey, PixelTypes.Rgba32)] - public void TgaDecoder_CanDecode_Uncompressed_MonoChrome(TestImageProvider provider) + [WithFile(Gray8Bit, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_Uncompressed_MonoChrome_8Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Gray8BitRle, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_RunLengthEncoded_MonoChrome_8Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Gray16Bit, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_Uncompressed_MonoChrome_16Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Gray16BitBottomLeft, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_Uncompressed_MonoChrome_WithBottomLeftOrigin_16Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Gray16BitBottomRight, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_Uncompressed_MonoChrome_WithBottomRightOrigin_16Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Gray16BitRle, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_RunLengthEncoded_MonoChrome_16Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Gray16BitRleBottomLeft, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_RunLengthEncoded_MonoChrome_WithBottomLeftOrigin_16Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Gray16BitRleBottomRight, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_RunLengthEncoded_MonoChrome_WithBottomRightOrigin_16Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Gray16BitRleTopRight, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_RunLengthEncoded_MonoChrome_WithTopRightOrigin_16Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { using (Image image = provider.GetImage(TgaDecoder)) @@ -127,18 +223,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } } - [Theory] - [WithFile(GreyRle, PixelTypes.Rgba32)] - public void TgaDecoder_CanDecode_RunLengthEncoded_MonoChrome(TestImageProvider provider) - where TPixel : unmanaged, IPixel - { - using (Image image = provider.GetImage(TgaDecoder)) - { - image.DebugSave(provider); - TgaTestUtils.CompareWithReferenceDecoder(provider, image); - } - } - [Theory] [WithFile(Bit16Rle, PixelTypes.Rgba32)] public void TgaDecoder_CanDecode_RunLengthEncoded_16Bit(TestImageProvider provider) diff --git a/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs index 00664de6e..c6b8b71f9 100644 --- a/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs @@ -25,10 +25,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga public static readonly TheoryData TgaBitsPerPixelFiles = new TheoryData { - { Grey, TgaBitsPerPixel.Pixel8 }, - { Bit32, TgaBitsPerPixel.Pixel32 }, - { Bit24, TgaBitsPerPixel.Pixel24 }, + { Gray8Bit, TgaBitsPerPixel.Pixel8 }, { Bit16, TgaBitsPerPixel.Pixel16 }, + { Bit24, TgaBitsPerPixel.Pixel24 }, + { Bit32, TgaBitsPerPixel.Pixel32 }, }; [Theory] @@ -37,14 +37,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga { var options = new TgaEncoder(); - TestFile testFile = TestFile.Create(imagePath); + var testFile = TestFile.Create(imagePath); using (Image input = testFile.CreateRgba32Image()) { using (var memStream = new MemoryStream()) { input.Save(memStream, options); memStream.Position = 0; - using (Image output = Image.Load(memStream)) + using (var output = Image.Load(memStream)) { TgaMetadata meta = output.Metadata.GetTgaMetadata(); Assert.Equal(bmpBitsPerPixel, meta.BitsPerPixel); diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 3e2f4aa6f..0622222cf 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -383,8 +383,15 @@ namespace SixLabors.ImageSharp.Tests public const string Bit24TopLeft = "Tga/targa_24bit_pal_origin_topleft.tga"; public const string Bit24RleTopLeft = "Tga/targa_24bit_rle_origin_topleft.tga"; public const string Bit32 = "Tga/targa_32bit.tga"; - public const string Grey = "Tga/targa_8bit.tga"; - public const string GreyRle = "Tga/targa_8bit_rle.tga"; + public const string Gray8Bit = "Tga/targa_8bit.tga"; + public const string Gray8BitRle = "Tga/targa_8bit_rle.tga"; + public const string Gray16Bit = "Tga/grayscale_a_UL.tga"; + public const string Gray16BitBottomLeft = "Tga/grayscale_a_LL.tga"; + public const string Gray16BitBottomRight = "Tga/grayscale_a_LR.tga"; + public const string Gray16BitRle = "Tga/grayscale_a_rle_UL.tga"; + public const string Gray16BitRleBottomLeft = "Tga/grayscale_a_rle_LL.tga"; + public const string Gray16BitRleBottomRight = "Tga/grayscale_a_rle_LR.tga"; + public const string Gray16BitRleTopRight = "Tga/grayscale_a_rle_UR.tga"; public const string Bit16Rle = "Tga/targa_16bit_rle.tga"; public const string Bit24Rle = "Tga/targa_24bit_rle.tga"; public const string Bit32Rle = "Tga/targa_32bit_rle.tga"; diff --git a/tests/Images/Input/Tga/grayscale_a_LL.tga b/tests/Images/Input/Tga/grayscale_a_LL.tga new file mode 100644 index 000000000..ebc378134 --- /dev/null +++ b/tests/Images/Input/Tga/grayscale_a_LL.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e90d280ddfde2d147dd68bacf7bb31e9133f8132adcbe50c841950d5a7834b8e +size 131116 diff --git a/tests/Images/Input/Tga/grayscale_a_LR.tga b/tests/Images/Input/Tga/grayscale_a_LR.tga new file mode 100644 index 000000000..1d142b5c1 --- /dev/null +++ b/tests/Images/Input/Tga/grayscale_a_LR.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:df0cd7261a98e87700e4f9c1328d73ee9f278c4e538895ab0a97b88392156523 +size 131116 diff --git a/tests/Images/Input/Tga/grayscale_a_UL.tga b/tests/Images/Input/Tga/grayscale_a_UL.tga new file mode 100644 index 000000000..bd6c25627 --- /dev/null +++ b/tests/Images/Input/Tga/grayscale_a_UL.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:debc2bb439a72f5cae3f0fdb525dbc0b3488abc27cee81d1eb73cb97765a07f3 +size 131116 diff --git a/tests/Images/Input/Tga/grayscale_a_rle_LL.tga b/tests/Images/Input/Tga/grayscale_a_rle_LL.tga new file mode 100644 index 000000000..3434cc86c --- /dev/null +++ b/tests/Images/Input/Tga/grayscale_a_rle_LL.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d65c2b9caf83b2eb063e820e15944621dec324f8278ae6b60b088dc380a2c40b +size 54102 diff --git a/tests/Images/Input/Tga/grayscale_a_rle_LR.tga b/tests/Images/Input/Tga/grayscale_a_rle_LR.tga new file mode 100644 index 000000000..75850f39c --- /dev/null +++ b/tests/Images/Input/Tga/grayscale_a_rle_LR.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0f7e06f04de22ecbf8fea1da72c6a6feb45161e92580e96ca5c4482ec3bc00de +size 54237 diff --git a/tests/Images/Input/Tga/grayscale_a_rle_UL.tga b/tests/Images/Input/Tga/grayscale_a_rle_UL.tga new file mode 100644 index 000000000..ed77308e5 --- /dev/null +++ b/tests/Images/Input/Tga/grayscale_a_rle_UL.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8555c8dcfa7ac65ad9f1d2389d82ee21dd90329b7200e10a457abc0f67d18ac8 +size 54295 diff --git a/tests/Images/Input/Tga/grayscale_a_rle_UR.tga b/tests/Images/Input/Tga/grayscale_a_rle_UR.tga new file mode 100644 index 000000000..04945dc61 --- /dev/null +++ b/tests/Images/Input/Tga/grayscale_a_rle_UR.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9abc35a5e6ef0aaa29a5d0bd7cef30281b1d94fec669e884cc382a2d73b359a0 +size 54052 From 96ff5b8b146c6d86e6c39ddc500167e74102f240 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 29 Mar 2020 13:24:00 +0200 Subject: [PATCH 38/58] Add additional tests for images which have bottom right and top right origin --- .../Formats/Tga/TgaDecoderTests.cs | 144 ++++++++++++++++++ tests/ImageSharp.Tests/TestImages.cs | 12 ++ tests/Images/Input/Tga/indexed_a_LL.tga | 3 + tests/Images/Input/Tga/indexed_a_LR.tga | 3 + tests/Images/Input/Tga/indexed_a_UL.tga | 3 + tests/Images/Input/Tga/indexed_a_UR.tga | 3 + tests/Images/Input/Tga/rgb_LR.tga | 3 + tests/Images/Input/Tga/rgb_UR.tga | 3 + tests/Images/Input/Tga/rgb_a_LR.tga | 3 + tests/Images/Input/Tga/rgb_a_UR.tga | 3 + tests/Images/Input/Tga/rgb_a_rle_LR.tga | 3 + tests/Images/Input/Tga/rgb_a_rle_UR.tga | 3 + tests/Images/Input/Tga/rgb_rle_LR.tga | 3 + tests/Images/Input/Tga/rgb_rle_UR.tga | 3 + 14 files changed, 192 insertions(+) create mode 100644 tests/Images/Input/Tga/indexed_a_LL.tga create mode 100644 tests/Images/Input/Tga/indexed_a_LR.tga create mode 100644 tests/Images/Input/Tga/indexed_a_UL.tga create mode 100644 tests/Images/Input/Tga/indexed_a_UR.tga create mode 100644 tests/Images/Input/Tga/rgb_LR.tga create mode 100644 tests/Images/Input/Tga/rgb_UR.tga create mode 100644 tests/Images/Input/Tga/rgb_a_LR.tga create mode 100644 tests/Images/Input/Tga/rgb_a_UR.tga create mode 100644 tests/Images/Input/Tga/rgb_a_rle_LR.tga create mode 100644 tests/Images/Input/Tga/rgb_a_rle_UR.tga create mode 100644 tests/Images/Input/Tga/rgb_rle_LR.tga create mode 100644 tests/Images/Input/Tga/rgb_rle_UR.tga diff --git a/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs index 2ce6eb3c2..5bfb38b74 100644 --- a/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs @@ -187,6 +187,30 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } } + [Theory] + [WithFile(Bit24TopRight, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_Uncompressed_WithTopRightOrigin_24Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Bit24BottomRight, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_Uncompressed_WithBottomRightOrigin_24Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + [Theory] [WithFile(Bit24RleTopLeft, PixelTypes.Rgba32)] public void TgaDecoder_CanDecode_RunLengthEncoded_WithTopLeftOrigin_24Bit(TestImageProvider provider) @@ -199,6 +223,30 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } } + [Theory] + [WithFile(Bit24RleTopRight, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_RunLengthEncoded_WithTopRightOrigin_24Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Bit24RleBottomRight, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_RunLengthEncoded_WithBottomRightOrigin_24Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + [Theory] [WithFile(Bit24TopLeft, PixelTypes.Rgba32)] public void TgaDecoder_CanDecode_Palette_WithTopLeftOrigin_24Bit(TestImageProvider provider) @@ -223,6 +271,30 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } } + [Theory] + [WithFile(Bit32BottomRight, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_Uncompressed_WithBottomRightOrigin_32Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Bit32TopRight, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_Uncompressed_WithTopRightOrigin_32Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + [Theory] [WithFile(Bit16Rle, PixelTypes.Rgba32)] public void TgaDecoder_CanDecode_RunLengthEncoded_16Bit(TestImageProvider provider) @@ -259,6 +331,30 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } } + [Theory] + [WithFile(Bit32RleTopRight, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_RunLengthEncoded_WithTopRightOrigin_32Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Bit32RleBottomRight, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_RunLengthEncoded_WithBottomRightOrigin_32Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + [Theory] [WithFile(Bit16Pal, PixelTypes.Rgba32)] public void TgaDecoder_CanDecode_WithPalette_16Bit(TestImageProvider provider) @@ -283,6 +379,54 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } } + [Theory] + [WithFile(Bit32Pal, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_WithPalette_32Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Bit32PalBottomLeft, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_WithPalette_WithBottomLeftOrigin_32Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Bit32PalBottomRight, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_WithPalette_WithBottomRightOrigin_32Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Bit32PalTopRight, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_WithPalette_WithTopRightOrigin_32Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + [Theory] [WithFile(NoAlphaBits16Bit, PixelTypes.Rgba32)] [WithFile(NoAlphaBits16BitRle, PixelTypes.Rgba32)] diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 0622222cf..197210f67 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -380,9 +380,21 @@ namespace SixLabors.ImageSharp.Tests public const string Bit16 = "Tga/targa_16bit.tga"; public const string Bit16PalRle = "Tga/ccm8.tga"; public const string Bit24 = "Tga/targa_24bit.tga"; + public const string Bit24TopRight = "Tga/rgb_UR.tga"; + public const string Bit24BottomRight = "Tga/rgb_LR.tga"; public const string Bit24TopLeft = "Tga/targa_24bit_pal_origin_topleft.tga"; public const string Bit24RleTopLeft = "Tga/targa_24bit_rle_origin_topleft.tga"; + public const string Bit24RleTopRight = "Tga/rgb_rle_UR.tga"; + public const string Bit24RleBottomRight = "Tga/rgb_rle_LR.tga"; public const string Bit32 = "Tga/targa_32bit.tga"; + public const string Bit32Pal = "Tga/indexed_a_UL.tga"; + public const string Bit32PalBottomLeft = "Tga/indexed_a_LL.tga"; + public const string Bit32PalBottomRight = "Tga/indexed_a_LR.tga"; + public const string Bit32PalTopRight = "Tga/indexed_a_UR.tga"; + public const string Bit32TopRight = "Tga/rgb_a_UR.tga"; + public const string Bit32BottomRight = "Tga/rgb_a_LR.tga"; + public const string Bit32RleTopRight = "Tga/rgb_a_rle_UR.tga"; + public const string Bit32RleBottomRight = "Tga/rgb_a_rle_LR.tga"; public const string Gray8Bit = "Tga/targa_8bit.tga"; public const string Gray8BitRle = "Tga/targa_8bit_rle.tga"; public const string Gray16Bit = "Tga/grayscale_a_UL.tga"; diff --git a/tests/Images/Input/Tga/indexed_a_LL.tga b/tests/Images/Input/Tga/indexed_a_LL.tga new file mode 100644 index 000000000..e074f253b --- /dev/null +++ b/tests/Images/Input/Tga/indexed_a_LL.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1522f4513cadd35869f39e171b1dccda9181da5b812d487e2a3e17308722d7c0 +size 66604 diff --git a/tests/Images/Input/Tga/indexed_a_LR.tga b/tests/Images/Input/Tga/indexed_a_LR.tga new file mode 100644 index 000000000..aa361fa74 --- /dev/null +++ b/tests/Images/Input/Tga/indexed_a_LR.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d01d5c89e772582a30ef9d528928cc313474a54b7f5530947a637adea95a4536 +size 66604 diff --git a/tests/Images/Input/Tga/indexed_a_UL.tga b/tests/Images/Input/Tga/indexed_a_UL.tga new file mode 100644 index 000000000..19b0b36fc --- /dev/null +++ b/tests/Images/Input/Tga/indexed_a_UL.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fa4d93b76ddcfa82a8ef02921e1c90dbd136de45608e7e7502c2d2256736f9ae +size 66604 diff --git a/tests/Images/Input/Tga/indexed_a_UR.tga b/tests/Images/Input/Tga/indexed_a_UR.tga new file mode 100644 index 000000000..9b783a88a --- /dev/null +++ b/tests/Images/Input/Tga/indexed_a_UR.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:feab3d418ab68eef0b40282de0e00c126fedff31f8657159799efef9b6f4a2af +size 66604 diff --git a/tests/Images/Input/Tga/rgb_LR.tga b/tests/Images/Input/Tga/rgb_LR.tga new file mode 100644 index 000000000..bb6a8a9c8 --- /dev/null +++ b/tests/Images/Input/Tga/rgb_LR.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a57a4f63dbe50b43e95cfcffff0ecf981de91268c44064b73c94c295f0909fea +size 196652 diff --git a/tests/Images/Input/Tga/rgb_UR.tga b/tests/Images/Input/Tga/rgb_UR.tga new file mode 100644 index 000000000..b7a7754fe --- /dev/null +++ b/tests/Images/Input/Tga/rgb_UR.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1dc5882241cd3513795cfcb207b7b4b6014585cf50504e01f968f1db9ad7d8d8 +size 196652 diff --git a/tests/Images/Input/Tga/rgb_a_LR.tga b/tests/Images/Input/Tga/rgb_a_LR.tga new file mode 100644 index 000000000..312af4c0d --- /dev/null +++ b/tests/Images/Input/Tga/rgb_a_LR.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0b91c063644c2f21f74fa88687a05f8730366e75a896bf21630af280abc9950b +size 262188 diff --git a/tests/Images/Input/Tga/rgb_a_UR.tga b/tests/Images/Input/Tga/rgb_a_UR.tga new file mode 100644 index 000000000..12d7b5a79 --- /dev/null +++ b/tests/Images/Input/Tga/rgb_a_UR.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9d88b70ad8878d44e29f680716670dd876771620264bdf2af9179284508fcc03 +size 262188 diff --git a/tests/Images/Input/Tga/rgb_a_rle_LR.tga b/tests/Images/Input/Tga/rgb_a_rle_LR.tga new file mode 100644 index 000000000..ceac831b8 --- /dev/null +++ b/tests/Images/Input/Tga/rgb_a_rle_LR.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0bcfe104b6c56ddaa06bfaca4a2a9b070e7af8f74dc433736d6b0e536bf3c0b6 +size 98317 diff --git a/tests/Images/Input/Tga/rgb_a_rle_UR.tga b/tests/Images/Input/Tga/rgb_a_rle_UR.tga new file mode 100644 index 000000000..e6eebbdaf --- /dev/null +++ b/tests/Images/Input/Tga/rgb_a_rle_UR.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cec69308cbfd13f1cae79462fcfd013655d27fb6386e60e6801a8fbb58685201 +size 97990 diff --git a/tests/Images/Input/Tga/rgb_rle_LR.tga b/tests/Images/Input/Tga/rgb_rle_LR.tga new file mode 100644 index 000000000..11146a812 --- /dev/null +++ b/tests/Images/Input/Tga/rgb_rle_LR.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0c21355f73ed5f78ec2835c3e8bb11b1d48bc5b360a804555a49a435077e8bcb +size 73337 diff --git a/tests/Images/Input/Tga/rgb_rle_UR.tga b/tests/Images/Input/Tga/rgb_rle_UR.tga new file mode 100644 index 000000000..4c9e540d3 --- /dev/null +++ b/tests/Images/Input/Tga/rgb_rle_UR.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f5d56b7e72b59624545b405406daeb9a578ff3da6e1ea99ee759ace6909da6d6 +size 73086 From 2b18c6c82e15684feb484c21adc2cfe2b936ca17 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 29 Mar 2020 14:08:18 +0200 Subject: [PATCH 39/58] Update Magick.Net to 7.15.5 --- src/ImageSharp/Formats/Tga/TgaDecoderCore.cs | 20 +++++++++---------- tests/Directory.Build.targets | 2 +- .../Formats/Tga/TgaTestUtils.cs | 1 + 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs index dd3b6a821..c8e23532f 100644 --- a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs +++ b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs @@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Formats.Tga /// /// A scratch buffer to reduce allocations. /// - private readonly byte[] buffer = new byte[4]; + private readonly byte[] scratchBuffer = new byte[4]; /// /// The metadata. @@ -430,19 +430,19 @@ namespace SixLabors.ImageSharp.Formats.Tga { for (int x = 0; x < width; x++) { - this.currentStream.Read(this.buffer, 0, 2); + this.currentStream.Read(this.scratchBuffer, 0, 2); if (!this.hasAlpha) { - this.buffer[1] |= 1 << 7; + this.scratchBuffer[1] |= 1 << 7; } if (this.fileHeader.ImageType == TgaImageType.BlackAndWhite) { - color.FromLa16(Unsafe.As(ref this.buffer[0])); + color.FromLa16(Unsafe.As(ref this.scratchBuffer[0])); } else { - color.FromBgra5551(Unsafe.As(ref this.buffer[0])); + color.FromBgra5551(Unsafe.As(ref this.scratchBuffer[0])); } int newX = InvertX(x, width, origin); @@ -497,8 +497,8 @@ namespace SixLabors.ImageSharp.Formats.Tga Span pixelSpan = pixels.GetRowSpan(newY); for (int x = 0; x < width; x++) { - this.currentStream.Read(this.buffer, 0, 3); - color.FromBgr24(Unsafe.As(ref this.buffer[0])); + this.currentStream.Read(this.scratchBuffer, 0, 3); + color.FromBgr24(Unsafe.As(ref this.scratchBuffer[0])); int newX = InvertX(x, width, origin); pixelSpan[newX] = color; } @@ -531,9 +531,9 @@ namespace SixLabors.ImageSharp.Formats.Tga where TPixel : unmanaged, IPixel { TPixel color = default; + bool isXInverted = origin == TgaImageOrigin.BottomRight || origin == TgaImageOrigin.TopRight; if (this.tgaMetadata.AlphaChannelBits == 8) { - bool isXInverted = origin == TgaImageOrigin.BottomRight || origin == TgaImageOrigin.TopRight; if (isXInverted) { for (int y = 0; y < height; y++) @@ -542,8 +542,8 @@ namespace SixLabors.ImageSharp.Formats.Tga Span pixelSpan = pixels.GetRowSpan(newY); for (int x = 0; x < width; x++) { - this.currentStream.Read(this.buffer, 0, 4); - color.FromBgra32(Unsafe.As(ref this.buffer[0])); + this.currentStream.Read(this.scratchBuffer, 0, 4); + color.FromBgra32(Unsafe.As(ref this.scratchBuffer[0])); int newX = InvertX(x, width, origin); pixelSpan[newX] = color; } diff --git a/tests/Directory.Build.targets b/tests/Directory.Build.targets index 3c3b3b5ec..df153c08b 100644 --- a/tests/Directory.Build.targets +++ b/tests/Directory.Build.targets @@ -29,7 +29,7 @@ - + diff --git a/tests/ImageSharp.Tests/Formats/Tga/TgaTestUtils.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaTestUtils.cs index cb3986b1f..c69cf2f20 100644 --- a/tests/ImageSharp.Tests/Formats/Tga/TgaTestUtils.cs +++ b/tests/ImageSharp.Tests/Formats/Tga/TgaTestUtils.cs @@ -44,6 +44,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga { using (var magickImage = new MagickImage(fileInfo)) { + magickImage.AutoOrient(); var result = new Image(configuration, magickImage.Width, magickImage.Height); Span resultPixels = result.GetPixelSpan(); From b89c099e714ebbb4200d98bf2862ee814dff7f92 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 29 Mar 2020 14:15:21 +0200 Subject: [PATCH 40/58] Simplify ReadBgra32 --- src/ImageSharp/Formats/Tga/TgaDecoderCore.cs | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs index c8e23532f..e9e7ce719 100644 --- a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs +++ b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs @@ -532,26 +532,8 @@ namespace SixLabors.ImageSharp.Formats.Tga { TPixel color = default; bool isXInverted = origin == TgaImageOrigin.BottomRight || origin == TgaImageOrigin.TopRight; - if (this.tgaMetadata.AlphaChannelBits == 8) + if (this.tgaMetadata.AlphaChannelBits == 8 && !isXInverted) { - if (isXInverted) - { - for (int y = 0; y < height; y++) - { - int newY = InvertY(y, height, origin); - Span pixelSpan = pixels.GetRowSpan(newY); - for (int x = 0; x < width; x++) - { - this.currentStream.Read(this.scratchBuffer, 0, 4); - color.FromBgra32(Unsafe.As(ref this.scratchBuffer[0])); - int newX = InvertX(x, width, origin); - pixelSpan[newX] = color; - } - } - - return; - } - using (IManagedByteBuffer row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 4, 0)) { for (int y = 0; y < height; y++) From 365c1661acba1301935c4e63a619026c81f974c8 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 29 Mar 2020 19:15:25 +0200 Subject: [PATCH 41/58] Update external for the 16bit gray tga images --- .../Formats/Tga/TgaDecoderTests.cs | 69 ++++++++++++------- tests/Images/External | 2 +- 2 files changed, 46 insertions(+), 25 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs index 5bfb38b74..d9f51701e 100644 --- a/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs @@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga [Theory] [WithFile(Gray8Bit, PixelTypes.Rgba32)] - public void TgaDecoder_CanDecode_Uncompressed_MonoChrome_8Bit(TestImageProvider provider) + public void TgaDecoder_CanDecode_Gray_8Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { using (Image image = provider.GetImage(TgaDecoder)) @@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga [Theory] [WithFile(Gray8BitRle, PixelTypes.Rgba32)] - public void TgaDecoder_CanDecode_RunLengthEncoded_MonoChrome_8Bit(TestImageProvider provider) + public void TgaDecoder_CanDecode_RunLengthEncoded_Gray_8Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { using (Image image = provider.GetImage(TgaDecoder)) @@ -45,91 +45,112 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga [Theory] [WithFile(Gray16Bit, PixelTypes.Rgba32)] - public void TgaDecoder_CanDecode_Uncompressed_MonoChrome_16Bit(TestImageProvider provider) + public void TgaDecoder_CanDecode_Gray_16Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { using (Image image = provider.GetImage(TgaDecoder)) { image.DebugSave(provider); - TgaTestUtils.CompareWithReferenceDecoder(provider, image); + + // Using here the reference output instead of the the reference decoder, + // because the reference decoder output seems not to be correct for 16bit gray images. + image.CompareToReferenceOutput(ImageComparer.Exact, provider); } } [Theory] [WithFile(Gray16BitBottomLeft, PixelTypes.Rgba32)] - public void TgaDecoder_CanDecode_Uncompressed_MonoChrome_WithBottomLeftOrigin_16Bit(TestImageProvider provider) + public void TgaDecoder_CanDecode_Gray_WithBottomLeftOrigin_16Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { using (Image image = provider.GetImage(TgaDecoder)) { image.DebugSave(provider); - TgaTestUtils.CompareWithReferenceDecoder(provider, image); + + // Using here the reference output instead of the the reference decoder, + // because the reference decoder output seems not to be correct for 16bit gray images. + image.CompareToReferenceOutput(ImageComparer.Exact, provider); } } [Theory] [WithFile(Gray16BitBottomRight, PixelTypes.Rgba32)] - public void TgaDecoder_CanDecode_Uncompressed_MonoChrome_WithBottomRightOrigin_16Bit(TestImageProvider provider) + public void TgaDecoder_CanDecode_Gray_WithBottomRightOrigin_16Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { using (Image image = provider.GetImage(TgaDecoder)) { image.DebugSave(provider); - TgaTestUtils.CompareWithReferenceDecoder(provider, image); + + // Using here the reference output instead of the the reference decoder, + // because the reference decoder output seems not to be correct for 16bit gray images. + image.CompareToReferenceOutput(ImageComparer.Exact, provider); } } [Theory] [WithFile(Gray16BitRle, PixelTypes.Rgba32)] - public void TgaDecoder_CanDecode_RunLengthEncoded_MonoChrome_16Bit(TestImageProvider provider) + public void TgaDecoder_CanDecode_RunLengthEncoded_Gray_16Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { using (Image image = provider.GetImage(TgaDecoder)) { image.DebugSave(provider); - TgaTestUtils.CompareWithReferenceDecoder(provider, image); + + // Using here the reference output instead of the the reference decoder, + // because the reference decoder output seems not to be correct for 16bit gray images. + image.CompareToReferenceOutput(ImageComparer.Exact, provider); } } [Theory] [WithFile(Gray16BitRleBottomLeft, PixelTypes.Rgba32)] - public void TgaDecoder_CanDecode_RunLengthEncoded_MonoChrome_WithBottomLeftOrigin_16Bit(TestImageProvider provider) + public void TgaDecoder_CanDecode_RunLengthEncoded_Gray_WithBottomLeftOrigin_16Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { using (Image image = provider.GetImage(TgaDecoder)) { image.DebugSave(provider); - TgaTestUtils.CompareWithReferenceDecoder(provider, image); + + // Using here the reference output instead of the the reference decoder, + // because the reference decoder output seems not to be correct for 16bit gray images. + image.CompareToReferenceOutput(ImageComparer.Exact, provider); } } [Theory] [WithFile(Gray16BitRleBottomRight, PixelTypes.Rgba32)] - public void TgaDecoder_CanDecode_RunLengthEncoded_MonoChrome_WithBottomRightOrigin_16Bit(TestImageProvider provider) + public void TgaDecoder_CanDecode_RunLengthEncoded_Gray_WithBottomRightOrigin_16Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { using (Image image = provider.GetImage(TgaDecoder)) { image.DebugSave(provider); - TgaTestUtils.CompareWithReferenceDecoder(provider, image); + + // Using here the reference output instead of the the reference decoder, + // because the reference decoder output seems not to be correct for 16bit gray images. + image.CompareToReferenceOutput(ImageComparer.Exact, provider); } } [Theory] [WithFile(Gray16BitRleTopRight, PixelTypes.Rgba32)] - public void TgaDecoder_CanDecode_RunLengthEncoded_MonoChrome_WithTopRightOrigin_16Bit(TestImageProvider provider) + public void TgaDecoder_CanDecode_RunLengthEncoded_Gray_WithTopRightOrigin_16Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { using (Image image = provider.GetImage(TgaDecoder)) { image.DebugSave(provider); - TgaTestUtils.CompareWithReferenceDecoder(provider, image); + + // Using here the reference output instead of the the reference decoder, + // because the reference decoder output seems not to be correct for 16bit gray images. + image.CompareToReferenceOutput(ImageComparer.Exact, provider); } } [Theory] [WithFile(Bit15, PixelTypes.Rgba32)] - public void TgaDecoder_CanDecode_Uncompressed_15Bit(TestImageProvider provider) + public void TgaDecoder_CanDecode_15Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { using (Image image = provider.GetImage(TgaDecoder)) @@ -153,7 +174,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga [Theory] [WithFile(Bit16, PixelTypes.Rgba32)] - public void TgaDecoder_CanDecode_Uncompressed_16Bit(TestImageProvider provider) + public void TgaDecoder_CanDecode_16Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { using (Image image = provider.GetImage(TgaDecoder)) @@ -177,7 +198,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga [Theory] [WithFile(Bit24, PixelTypes.Rgba32)] - public void TgaDecoder_CanDecode_Uncompressed_24Bit(TestImageProvider provider) + public void TgaDecoder_CanDecode_24Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { using (Image image = provider.GetImage(TgaDecoder)) @@ -189,7 +210,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga [Theory] [WithFile(Bit24TopRight, PixelTypes.Rgba32)] - public void TgaDecoder_CanDecode_Uncompressed_WithTopRightOrigin_24Bit(TestImageProvider provider) + public void TgaDecoder_CanDecode_WithTopRightOrigin_24Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { using (Image image = provider.GetImage(TgaDecoder)) @@ -201,7 +222,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga [Theory] [WithFile(Bit24BottomRight, PixelTypes.Rgba32)] - public void TgaDecoder_CanDecode_Uncompressed_WithBottomRightOrigin_24Bit(TestImageProvider provider) + public void TgaDecoder_CanDecode_WithBottomRightOrigin_24Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { using (Image image = provider.GetImage(TgaDecoder)) @@ -261,7 +282,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga [Theory] [WithFile(Bit32, PixelTypes.Rgba32)] - public void TgaDecoder_CanDecode_Uncompressed_32Bit(TestImageProvider provider) + public void TgaDecoder_CanDecode_32Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { using (Image image = provider.GetImage(TgaDecoder)) @@ -273,7 +294,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga [Theory] [WithFile(Bit32BottomRight, PixelTypes.Rgba32)] - public void TgaDecoder_CanDecode_Uncompressed_WithBottomRightOrigin_32Bit(TestImageProvider provider) + public void TgaDecoder_CanDecode_WithBottomRightOrigin_32Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { using (Image image = provider.GetImage(TgaDecoder)) @@ -285,7 +306,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga [Theory] [WithFile(Bit32TopRight, PixelTypes.Rgba32)] - public void TgaDecoder_CanDecode_Uncompressed_WithTopRightOrigin_32Bit(TestImageProvider provider) + public void TgaDecoder_CanDecode_WithTopRightOrigin_32Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { using (Image image = provider.GetImage(TgaDecoder)) diff --git a/tests/Images/External b/tests/Images/External index 985e050aa..6f81d7a95 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 985e050aa7ac11830ae7a178ca2283f8b6307e4c +Subproject commit 6f81d7a95e285b1449efd4319a5cc7b071dec679 From 4daa5f5a441ccc272aab4a3abfc9567437e59c31 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 29 Mar 2020 18:49:45 +0100 Subject: [PATCH 42/58] Make deflater constants internal --- src/ImageSharp/Formats/Png/Zlib/DeflaterConstants.cs | 4 +--- src/ImageSharp/Formats/Png/Zlib/DeflaterEngine.cs | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/Formats/Png/Zlib/DeflaterConstants.cs b/src/ImageSharp/Formats/Png/Zlib/DeflaterConstants.cs index 67e8c6900..a3a87a32f 100644 --- a/src/ImageSharp/Formats/Png/Zlib/DeflaterConstants.cs +++ b/src/ImageSharp/Formats/Png/Zlib/DeflaterConstants.cs @@ -3,15 +3,13 @@ // using System; -using System.Collections.Generic; -using System.Text; namespace SixLabors.ImageSharp.Formats.Png.Zlib { /// /// This class contains constants used for deflation. /// - public static class DeflaterConstants + internal static class DeflaterConstants { /// /// Set to true to enable debugging diff --git a/src/ImageSharp/Formats/Png/Zlib/DeflaterEngine.cs b/src/ImageSharp/Formats/Png/Zlib/DeflaterEngine.cs index 7be794b5e..1a8bb4ab0 100644 --- a/src/ImageSharp/Formats/Png/Zlib/DeflaterEngine.cs +++ b/src/ImageSharp/Formats/Png/Zlib/DeflaterEngine.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib /// /// Strategies for deflater /// - public enum DeflateStrategy + internal enum DeflateStrategy { /// /// The default strategy From 8b3fc67c8ce1faf9a3df501a4a53fe1500292004 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 29 Mar 2020 18:49:57 +0100 Subject: [PATCH 43/58] Move GeometryUtilities --- src/ImageSharp/GeometryUtilities.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ImageSharp/GeometryUtilities.cs b/src/ImageSharp/GeometryUtilities.cs index 43c46f181..00fa5b97f 100644 --- a/src/ImageSharp/GeometryUtilities.cs +++ b/src/ImageSharp/GeometryUtilities.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. using System; using System.Runtime.CompilerServices; -namespace SixLabors +namespace SixLabors.ImageSharp { /// /// Utility class for common geometric functions. @@ -31,4 +31,4 @@ namespace SixLabors [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float RadianToDegree(float radian) => radian / (MathF.PI / 180F); } -} \ No newline at end of file +} From 20d97eaffb2d4bed3a9c0b51736b955bf235b065 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 29 Mar 2020 22:38:13 +0100 Subject: [PATCH 44/58] Update colorspace namespaces --- src/ImageSharp/ColorSpaces/Companding/LCompanding.cs | 4 ++-- .../ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs | 6 ++---- .../ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs | 3 +-- .../ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs | 6 ++---- .../ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs | 6 ++---- .../ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs | 4 +--- .../ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs | 6 ++---- .../ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs | 6 ++---- .../ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs | 6 ++---- .../ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs | 6 ++---- .../ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs | 6 ++---- .../ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs | 4 +--- .../ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs | 6 ++---- .../ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs | 6 ++---- .../ColorSpaces/Conversion/ColorSpaceConverter.cs | 5 ++--- .../ColorSpaces/Conversion/ColorSpaceConverterOptions.cs | 5 ++--- .../Implementation}/CieXyChromaticityCoordinates.cs | 6 +++--- .../Implementation/Converters/CIeLchToCieLabConverter.cs | 2 +- .../Implementation/Converters/CieLabToCieLchConverter.cs | 6 +++--- .../Implementation/Converters/CieLabToCieXyzConverter.cs | 6 +++--- .../Implementation/Converters/CieLchuvToCieLuvConverter.cs | 6 +++--- .../Implementation/Converters/CieLuvToCieLchuvConverter.cs | 6 +++--- .../Implementation/Converters/CieLuvToCieXyzConverter.cs | 2 +- .../Implementation/Converters/CieXyzAndCieXyyConverter.cs | 2 +- .../Converters/CieXyzAndHunterLabConverterBase.cs | 2 +- .../Implementation/Converters/CieXyzAndLmsConverter.cs | 2 +- .../Implementation/Converters/CieXyzToCieLabConverter.cs | 2 +- .../Implementation/Converters/CieXyzToCieLuvConverter.cs | 2 +- .../Implementation/Converters/CieXyzToHunterLabConverter.cs | 2 +- .../Implementation/Converters/CieXyzToLinearRgbConverter.cs | 2 +- .../Implementation/Converters/CmykAndRgbConverter.cs | 2 +- .../Implementation/Converters/HslAndRgbConverter.cs | 2 +- .../Implementation/Converters/HsvAndRgbConverter.cs | 4 ++-- .../Implementation/Converters/HunterLabToCieXyzConverter.cs | 6 +++--- .../Converters/LinearRgbAndCieXyzConverterBase.cs | 6 +++--- .../Implementation/Converters/LinearRgbToCieXyzConverter.cs | 6 +++--- .../Implementation/Converters/LinearRgbToRgbConverter.cs | 6 +++--- .../Implementation/Converters/RgbToLinearRgbConverter.cs | 6 +++--- .../Implementation/Converters/YCbCrAndRgbConverter.cs | 2 +- .../Conversion/{ => Implementation}/IChromaticAdaptation.cs | 0 .../Conversion/Implementation/LmsAdaptationMatrix.cs | 6 +++--- .../Implementation/RGBPrimariesChromaticityCoordinates.cs | 4 ++-- .../{ => Implementation}/VonKriesChromaticAdaptation.cs | 5 ++--- .../Implementation/WorkingSpaces/GammaWorkingSpace.cs | 6 +++--- .../Implementation/WorkingSpaces/LWorkingSpace.cs | 6 +++--- .../Implementation/WorkingSpaces/Rec2020WorkingSpace.cs | 6 +++--- .../Implementation/WorkingSpaces/Rec709WorkingSpace.cs | 6 +++--- .../Implementation/WorkingSpaces/RgbWorkingSpace.cs | 6 +++--- .../Implementation/WorkingSpaces/SRgbWorkingSpace.cs | 4 ++-- src/ImageSharp/ColorSpaces/LinearRgb.cs | 6 +++--- src/ImageSharp/ColorSpaces/Rgb.cs | 6 +++--- src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs | 6 +++--- .../Colorspaces/CieXyChromaticityCoordinatesTests.cs | 2 +- .../Colorspaces/Conversion/ApproximateColorspaceComparer.cs | 2 +- .../Conversion/CieLabAndCieLchConversionTests.cs | 4 ++-- .../Conversion/CieLabAndCieLchuvConversionTests.cs | 4 ++-- .../Conversion/CieLabAndCieXyyConversionTests.cs | 4 ++-- .../Colorspaces/Conversion/ColorConverterAdaptTest.cs | 3 +-- .../Colorspaces/StringRepresentationTests.cs | 1 + 59 files changed, 116 insertions(+), 144 deletions(-) rename src/ImageSharp/ColorSpaces/{ => Conversion/Implementation}/CieXyChromaticityCoordinates.cs (97%) rename src/ImageSharp/ColorSpaces/Conversion/{ => Implementation}/IChromaticAdaptation.cs (100%) rename src/ImageSharp/ColorSpaces/Conversion/{ => Implementation}/VonKriesChromaticAdaptation.cs (97%) diff --git a/src/ImageSharp/ColorSpaces/Companding/LCompanding.cs b/src/ImageSharp/ColorSpaces/Companding/LCompanding.cs index 80b8aee7e..981e32f8e 100644 --- a/src/ImageSharp/ColorSpaces/Companding/LCompanding.cs +++ b/src/ImageSharp/ColorSpaces/Companding/LCompanding.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; @@ -35,4 +35,4 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding public static float Compress(float channel) => 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/Conversion/ColorSpaceConverter.Adapt.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs index 892c0d5e3..2b9073814 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs @@ -1,8 +1,6 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; - namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// @@ -157,4 +155,4 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToRgb(linearOutput); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs index 273c9be91..69d0c1bed 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs @@ -1,10 +1,9 @@ -// 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.ImageSharp.ColorSpaces.Conversion.Implementation; namespace SixLabors.ImageSharp.ColorSpaces.Conversion { diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs index 7f4abfa7b..52f872430 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs @@ -1,12 +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.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; - namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// @@ -447,4 +445,4 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs index 6ca40af03..2588561c8 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs @@ -1,12 +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.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; - namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// @@ -447,4 +445,4 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs index b790712c5..867b44a78 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs @@ -1,12 +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.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; - namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs index d03c10a01..344b83254 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs @@ -1,12 +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.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; - namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// @@ -440,4 +438,4 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } } } -} \ 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 21d576fb4..8362432ab 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs @@ -1,12 +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.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; - namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// @@ -475,4 +473,4 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.linearRgbToCieXyzConverter = new LinearRgbToCieXyzConverter(workingSpace); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs index 00e20e25b..3ff9ac85f 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs @@ -1,12 +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.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; - namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// @@ -440,4 +438,4 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs index 76175f1cb..7c7436391 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs @@ -1,12 +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.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; - namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// @@ -440,4 +438,4 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs index e64beb3e4..e60ad5e20 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs @@ -1,12 +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.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; - namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// @@ -440,4 +438,4 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs index 5b312f4f8..b2c23b5a4 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs @@ -1,12 +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.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; - namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs index fc5665e5c..e83dcb125 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs @@ -1,12 +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.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; - namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// @@ -438,4 +436,4 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.Adapt(rgb); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs index 68cd34bf2..4ea1d8d8f 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs @@ -1,12 +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.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; - namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// @@ -407,4 +405,4 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// The public YCbCr ToYCbCr(in Rgb color) => YCbCrAndRgbConverter.Convert(color); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs index bcbd64c77..05d3b2f2d 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs @@ -1,8 +1,7 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System.Numerics; -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; namespace SixLabors.ImageSharp.ColorSpaces.Conversion { @@ -58,4 +57,4 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion this.cieXyzToLinearRgbConverter = new CieXyzToLinearRgbConverter(this.targetRgbWorkingSpace); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverterOptions.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverterOptions.cs index 65fe79994..27dd989ad 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverterOptions.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverterOptions.cs @@ -1,8 +1,7 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System.Numerics; -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; namespace SixLabors.ImageSharp.ColorSpaces.Conversion { @@ -52,4 +51,4 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// public Matrix4x4 LmsAdaptationMatrix { get; set; } = CieXyzAndLmsConverter.DefaultTransformationMatrix; } -} \ No newline at end of file +} diff --git a/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyChromaticityCoordinates.cs similarity index 97% rename from src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs rename to src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyChromaticityCoordinates.cs index 8e14274f6..1e774fe67 100644 --- a/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyChromaticityCoordinates.cs @@ -1,11 +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.Runtime.CompilerServices; // ReSharper disable CompareOfFloatsByEqualityOperator -namespace SixLabors.ImageSharp.ColorSpaces +namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Represents the coordinates of CIEXY chromaticity space. @@ -76,4 +76,4 @@ namespace SixLabors.ImageSharp.ColorSpaces [MethodImpl(InliningOptions.ShortMethod)] public bool Equals(CieXyChromaticityCoordinates other) => this.X.Equals(other.X) && this.Y.Equals(other.Y); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CIeLchToCieLabConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CIeLchToCieLabConverter.cs index 40d8c5bc6..014ca1abb 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CIeLchToCieLabConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CIeLchToCieLabConverter.cs @@ -4,7 +4,7 @@ using System; using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation +namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Converts from to . diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieLchConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieLchConverter.cs index 2b859205a..e1bf04aa7 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieLchConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieLchConverter.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. using System; using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation +namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Converts from to . @@ -38,4 +38,4 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation return new CieLch(l, c, hDegrees, input.WhitePoint); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieXyzConverter.cs index dfbbc8f0c..bafd0df47 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieXyzConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieXyzConverter.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. using System.Numerics; using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation +namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Converts from to . @@ -41,4 +41,4 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation return new CieXyz(xyz); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLchuvToCieLuvConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLchuvToCieLuvConverter.cs index ba5b8bfb7..2dae2669f 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLchuvToCieLuvConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLchuvToCieLuvConverter.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. using System; using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation +namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Converts from to . @@ -30,4 +30,4 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation return new CieLuv(l, u, v, input.WhitePoint); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieLchuvConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieLchuvConverter.cs index 3c7d356a5..29fdae69c 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieLchuvConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieLchuvConverter.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. using System; using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation +namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Converts from to . @@ -38,4 +38,4 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation return new CieLchuv(l, c, hDegrees, input.WhitePoint); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieXyzConverter.cs index 33f3ec3d3..09040c98c 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieXyzConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieXyzConverter.cs @@ -3,7 +3,7 @@ using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation +namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Converts from to . diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndCieXyyConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndCieXyyConverter.cs index 7767b7b44..f704d1a34 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndCieXyyConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndCieXyyConverter.cs @@ -4,7 +4,7 @@ using System; using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation +namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Color converter between CIE XYZ and CIE xyY. diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndHunterLabConverterBase.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndHunterLabConverterBase.cs index 1cd511e81..68f6c495d 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndHunterLabConverterBase.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndHunterLabConverterBase.cs @@ -3,7 +3,7 @@ using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation +namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// The base class for converting between and color spaces. diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndLmsConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndLmsConverter.cs index f860652b1..a22b097d2 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndLmsConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndLmsConverter.cs @@ -4,7 +4,7 @@ using System.Numerics; using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation +namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Color converter between and diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLabConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLabConverter.cs index c155087ff..4f4aa8482 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLabConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLabConverter.cs @@ -4,7 +4,7 @@ using System; using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation +namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Converts from to . diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLuvConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLuvConverter.cs index 7f2bb0cf6..96ec96b49 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLuvConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLuvConverter.cs @@ -4,7 +4,7 @@ using System; using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation +namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Converts from to . diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToHunterLabConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToHunterLabConverter.cs index f21235d06..881d3d919 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToHunterLabConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToHunterLabConverter.cs @@ -4,7 +4,7 @@ using System; using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation +namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Color converter between and diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToLinearRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToLinearRgbConverter.cs index 6497e3060..25558537a 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToLinearRgbConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToLinearRgbConverter.cs @@ -4,7 +4,7 @@ using System.Numerics; using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation +namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Color converter between and diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CmykAndRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CmykAndRgbConverter.cs index 0700dab43..8e92ced94 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CmykAndRgbConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CmykAndRgbConverter.cs @@ -5,7 +5,7 @@ using System; using System.Numerics; using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation +namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Color converter between and . diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HslAndRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HslAndRgbConverter.cs index 97465e526..5dbd23b8b 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HslAndRgbConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HslAndRgbConverter.cs @@ -4,7 +4,7 @@ using System; using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation +namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Color converter between HSL and Rgb diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HsvAndRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HsvAndRgbConverter.cs index 20ada7e7d..04ab0480b 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HsvAndRgbConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HsvAndRgbConverter.cs @@ -4,7 +4,7 @@ using System; using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation +namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Color converter between HSV and Rgb @@ -127,4 +127,4 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation return new Hsv(h, s, v); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs index 4d6808e6c..795db7e2c 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.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. using System; using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation +namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Color converter between and @@ -36,4 +36,4 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation return new CieXyz(x, y, z); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbAndCieXyzConverterBase.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbAndCieXyzConverterBase.cs index bdf451cd3..105817075 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbAndCieXyzConverterBase.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbAndCieXyzConverterBase.cs @@ -1,9 +1,9 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System.Numerics; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation +namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Provides base methods for converting between and color spaces. @@ -74,4 +74,4 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation }; } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToCieXyzConverter.cs index 21a96071a..adaad50fe 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToCieXyzConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToCieXyzConverter.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. using System.Numerics; using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation +namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Color converter between and @@ -50,4 +50,4 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation return new CieXyz(vector); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToRgbConverter.cs index 845443093..54081b639 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToRgbConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToRgbConverter.cs @@ -1,9 +1,9 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation +namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Color converter between and . @@ -25,4 +25,4 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation 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 4ddbe42e5..bc8c6f030 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/RgbToLinearRgbConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/RgbToLinearRgbConverter.cs @@ -1,9 +1,9 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation +namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Color converter between Rgb and LinearRgb. @@ -25,4 +25,4 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation 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 ee15ffa50..0821c05c3 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/YCbCrAndRgbConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/YCbCrAndRgbConverter.cs @@ -5,7 +5,7 @@ using System; using System.Numerics; using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation +namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Color converter between and diff --git a/src/ImageSharp/ColorSpaces/Conversion/IChromaticAdaptation.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/IChromaticAdaptation.cs similarity index 100% rename from src/ImageSharp/ColorSpaces/Conversion/IChromaticAdaptation.cs rename to src/ImageSharp/ColorSpaces/Conversion/Implementation/IChromaticAdaptation.cs diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/LmsAdaptationMatrix.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/LmsAdaptationMatrix.cs index 37e4b1a1a..8b8e4ab57 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/LmsAdaptationMatrix.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/LmsAdaptationMatrix.cs @@ -1,9 +1,9 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System.Numerics; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation +namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Matrices used for transformation from to , defining the cone response domain. @@ -131,4 +131,4 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation M44 = 1F }); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/RGBPrimariesChromaticityCoordinates.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/RGBPrimariesChromaticityCoordinates.cs index 8871d0465..03378f431 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/RGBPrimariesChromaticityCoordinates.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/RGBPrimariesChromaticityCoordinates.cs @@ -1,9 +1,9 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation +namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Represents the chromaticity coordinates of RGB primaries. diff --git a/src/ImageSharp/ColorSpaces/Conversion/VonKriesChromaticAdaptation.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/VonKriesChromaticAdaptation.cs similarity index 97% rename from src/ImageSharp/ColorSpaces/Conversion/VonKriesChromaticAdaptation.cs rename to src/ImageSharp/ColorSpaces/Conversion/Implementation/VonKriesChromaticAdaptation.cs index a4d96d19e..7589a1d57 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/VonKriesChromaticAdaptation.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/VonKriesChromaticAdaptation.cs @@ -1,11 +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.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; namespace SixLabors.ImageSharp.ColorSpaces.Conversion { @@ -99,4 +98,4 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs index 6caca54cd..c7020723b 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs @@ -1,11 +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.Runtime.CompilerServices; using SixLabors.ImageSharp.ColorSpaces.Companding; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation +namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// The gamma working space. @@ -63,4 +63,4 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation this.ChromaticityCoordinates, this.Gamma); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/LWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/LWorkingSpace.cs index a2eb42ad0..d9c436527 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/LWorkingSpace.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/LWorkingSpace.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. using System.Runtime.CompilerServices; using SixLabors.ImageSharp.ColorSpaces.Companding; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation +namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// L* working space. @@ -29,4 +29,4 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation [MethodImpl(InliningOptions.ShortMethod)] public override float Expand(float channel) => LCompanding.Expand(channel); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec2020WorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec2020WorkingSpace.cs index a794b3dda..4698534db 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec2020WorkingSpace.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec2020WorkingSpace.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. using System.Runtime.CompilerServices; using SixLabors.ImageSharp.ColorSpaces.Companding; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation +namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Rec. 2020 (ITU-R Recommendation BT.2020F) working space. @@ -29,4 +29,4 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation [MethodImpl(InliningOptions.ShortMethod)] public override float Expand(float channel) => Rec2020Companding.Expand(channel); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec709WorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec709WorkingSpace.cs index ffa9699bc..80b635cad 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec709WorkingSpace.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec709WorkingSpace.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. using System.Runtime.CompilerServices; using SixLabors.ImageSharp.ColorSpaces.Companding; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation +namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Rec. 709 (ITU-R Recommendation BT.709) working space. @@ -29,4 +29,4 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation [MethodImpl(InliningOptions.ShortMethod)] public override float Expand(float channel) => Rec709Companding.Expand(channel); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/RgbWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/RgbWorkingSpace.cs index a97ae22b1..2e5a5a4eb 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/RgbWorkingSpace.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/RgbWorkingSpace.cs @@ -1,9 +1,9 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation +namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Base class for all implementations of . @@ -81,4 +81,4 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation return HashCode.Combine(this.WhitePoint, this.ChromaticityCoordinates); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/SRgbWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/SRgbWorkingSpace.cs index c3d850251..c9246f510 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/SRgbWorkingSpace.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/SRgbWorkingSpace.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. using System.Runtime.CompilerServices; using SixLabors.ImageSharp.ColorSpaces.Companding; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation +namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// The sRgb working space. diff --git a/src/ImageSharp/ColorSpaces/LinearRgb.cs b/src/ImageSharp/ColorSpaces/LinearRgb.cs index 7ef50e9c4..c120ef114 100644 --- a/src/ImageSharp/ColorSpaces/LinearRgb.cs +++ b/src/ImageSharp/ColorSpaces/LinearRgb.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. using System; using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; +using SixLabors.ImageSharp.ColorSpaces.Conversion; namespace SixLabors.ImageSharp.ColorSpaces { @@ -143,4 +143,4 @@ namespace SixLabors.ImageSharp.ColorSpaces && this.B.Equals(other.B); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/ColorSpaces/Rgb.cs b/src/ImageSharp/ColorSpaces/Rgb.cs index bb71deba3..3c26b7733 100644 --- a/src/ImageSharp/ColorSpaces/Rgb.cs +++ b/src/ImageSharp/ColorSpaces/Rgb.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. using System; using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; +using SixLabors.ImageSharp.ColorSpaces.Conversion; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.ColorSpaces @@ -164,4 +164,4 @@ namespace SixLabors.ImageSharp.ColorSpaces && this.B.Equals(other.B); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs b/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs index 3f40fa4f5..152c7ee0b 100644 --- a/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs +++ b/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs @@ -1,8 +1,8 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.ColorSpaces.Companding; -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; +using SixLabors.ImageSharp.ColorSpaces.Conversion; // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.ColorSpaces @@ -112,4 +112,4 @@ namespace SixLabors.ImageSharp.ColorSpaces /// 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/tests/ImageSharp.Tests/Colorspaces/CieXyChromaticityCoordinatesTests.cs b/tests/ImageSharp.Tests/Colorspaces/CieXyChromaticityCoordinatesTests.cs index 4811a66d4..418893f35 100644 --- a/tests/ImageSharp.Tests/Colorspaces/CieXyChromaticityCoordinatesTests.cs +++ b/tests/ImageSharp.Tests/Colorspaces/CieXyChromaticityCoordinatesTests.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; using Xunit; namespace SixLabors.ImageSharp.Tests.Colorspaces diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/ApproximateColorspaceComparer.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/ApproximateColorspaceComparer.cs index feb3b38f0..2046cdfdc 100644 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/ApproximateColorspaceComparer.cs +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/ApproximateColorspaceComparer.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; +using SixLabors.ImageSharp.ColorSpaces.Conversion; namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion { diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieLchConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieLchConversionTests.cs index 38c0c21bc..50d831fd9 100644 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieLchConversionTests.cs +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieLchConversionTests.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; @@ -92,4 +92,4 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion } } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieLchuvConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieLchuvConversionTests.cs index 96628977f..471610eba 100644 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieLchuvConversionTests.cs +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieLchuvConversionTests.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; @@ -80,4 +80,4 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion } } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieXyyConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieXyyConversionTests.cs index f7dc365b8..e00765832 100644 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieXyyConversionTests.cs +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieXyyConversionTests.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; @@ -76,4 +76,4 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion } } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/ColorConverterAdaptTest.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/ColorConverterAdaptTest.cs index 326777f3c..104b1f4b2 100644 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/ColorConverterAdaptTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/ColorConverterAdaptTest.cs @@ -3,7 +3,6 @@ using SixLabors.ImageSharp.ColorSpaces; using SixLabors.ImageSharp.ColorSpaces.Conversion; -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; using Xunit; namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion @@ -178,4 +177,4 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion Assert.Equal(expected, actual, ColorSpaceComparer); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Colorspaces/StringRepresentationTests.cs b/tests/ImageSharp.Tests/Colorspaces/StringRepresentationTests.cs index 211b98abb..df9659994 100644 --- a/tests/ImageSharp.Tests/Colorspaces/StringRepresentationTests.cs +++ b/tests/ImageSharp.Tests/Colorspaces/StringRepresentationTests.cs @@ -3,6 +3,7 @@ using System.Numerics; using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; using Xunit; namespace SixLabors.ImageSharp.Tests.Colorspaces From 985c1805e9793e31f178caf946ac97cfba223575 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 30 Mar 2020 18:42:41 +0200 Subject: [PATCH 45/58] Add additional tga test cases --- .../Formats/Tga/TgaDecoderTests.cs | 85 +++++++++++++++++-- .../Formats/Tga/TgaEncoderTests.cs | 6 +- tests/ImageSharp.Tests/TestImages.cs | 42 ++++++--- tests/Images/External | 2 +- tests/Images/Input/Tga/grayscale_LL.tga | 3 + tests/Images/Input/Tga/grayscale_UR.tga | 3 + tests/Images/Input/Tga/grayscale_a_UR.tga | 3 + tests/Images/Input/Tga/rgb24_top_left.tga | 3 + tests/Images/Input/Tga/rgb_a_LL.tga | 3 + 9 files changed, 127 insertions(+), 23 deletions(-) create mode 100644 tests/Images/Input/Tga/grayscale_LL.tga create mode 100644 tests/Images/Input/Tga/grayscale_UR.tga create mode 100644 tests/Images/Input/Tga/grayscale_a_UR.tga create mode 100644 tests/Images/Input/Tga/rgb24_top_left.tga create mode 100644 tests/Images/Input/Tga/rgb_a_LL.tga diff --git a/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs index d9f51701e..ccd00b49f 100644 --- a/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs @@ -31,6 +31,42 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } } + [Theory] + [WithFile(Gray8BitBottomLeft, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_Gray_WithBottomLeftOrigin_8Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Gray8BitTopRight, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_Gray_WithTopRightOrigin_8Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Gray8BitBottomRight, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_Gray_WithBottomRightOrigin_8Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + [Theory] [WithFile(Gray8BitRle, PixelTypes.Rgba32)] public void TgaDecoder_CanDecode_RunLengthEncoded_Gray_8Bit(TestImageProvider provider) @@ -88,6 +124,21 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } } + [Theory] + [WithFile(Gray16BitTopRight, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_Gray_WithTopRightOrigin_16Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + + // Using here the reference output instead of the the reference decoder, + // because the reference decoder output seems not to be correct for 16bit gray images. + image.CompareToReferenceOutput(ImageComparer.Exact, provider); + } + } + [Theory] [WithFile(Gray16BitRle, PixelTypes.Rgba32)] public void TgaDecoder_CanDecode_RunLengthEncoded_Gray_16Bit(TestImageProvider provider) @@ -173,8 +224,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } [Theory] - [WithFile(Bit16, PixelTypes.Rgba32)] - public void TgaDecoder_CanDecode_16Bit(TestImageProvider provider) + [WithFile(Bit16BottomLeft, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_WithBottomLeftOrigin_16Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { using (Image image = provider.GetImage(TgaDecoder)) @@ -208,6 +259,18 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } } + [Theory] + [WithFile(Bit24BottomLeft, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_WithBottomLeftOrigin_24Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + [Theory] [WithFile(Bit24TopRight, PixelTypes.Rgba32)] public void TgaDecoder_CanDecode_WithTopRightOrigin_24Bit(TestImageProvider provider) @@ -292,6 +355,18 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } } + [Theory] + [WithFile(Bit32BottomLeft, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_WithBottomLeftOrigin_32Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + [Theory] [WithFile(Bit32BottomRight, PixelTypes.Rgba32)] public void TgaDecoder_CanDecode_WithBottomRightOrigin_32Bit(TestImageProvider provider) @@ -477,8 +552,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } [Theory] - [WithFile(Bit16, PixelTypes.Rgba32)] - [WithFile(Bit24, PixelTypes.Rgba32)] + [WithFile(Bit16BottomLeft, PixelTypes.Rgba32)] + [WithFile(Bit24BottomLeft, PixelTypes.Rgba32)] [WithFile(Bit32, PixelTypes.Rgba32)] public void TgaDecoder_DegenerateMemoryRequest_ShouldTranslateTo_ImageFormatException(TestImageProvider provider) where TPixel : unmanaged, IPixel @@ -489,7 +564,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } [Theory] - [WithFile(Bit24, PixelTypes.Rgba32)] + [WithFile(Bit24BottomLeft, PixelTypes.Rgba32)] [WithFile(Bit32, PixelTypes.Rgba32)] public void TgaDecoder_CanDecode_WithLimitedAllocatorBufferCapacity(TestImageProvider provider) where TPixel : unmanaged, IPixel diff --git a/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs index c6b8b71f9..4e3e2d24c 100644 --- a/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs @@ -26,8 +26,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga new TheoryData { { Gray8Bit, TgaBitsPerPixel.Pixel8 }, - { Bit16, TgaBitsPerPixel.Pixel16 }, - { Bit24, TgaBitsPerPixel.Pixel24 }, + { Bit16BottomLeft, TgaBitsPerPixel.Pixel16 }, + { Bit24BottomLeft, TgaBitsPerPixel.Pixel24 }, { Bit32, TgaBitsPerPixel.Pixel32 }, }; @@ -124,7 +124,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga [Theory] [WithFile(Bit32, PixelTypes.Rgba32, TgaBitsPerPixel.Pixel32)] - [WithFile(Bit24, PixelTypes.Rgba32, TgaBitsPerPixel.Pixel24)] + [WithFile(Bit24BottomLeft, PixelTypes.Rgba32, TgaBitsPerPixel.Pixel24)] public void TgaEncoder_WorksWithDiscontiguousBuffers(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel) where TPixel : unmanaged, IPixel { diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 197210f67..38ef8d9e2 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -375,40 +375,54 @@ namespace SixLabors.ImageSharp.Tests public static class Tga { + public const string Gray8Bit = "Tga/targa_8bit.tga"; + public const string Gray8BitBottomLeft = "Tga/grayscale_LL.tga"; + public const string Gray8BitTopRight = "Tga/grayscale_UR.tga"; + public const string Gray8BitBottomRight = "Tga/grayscale_UR.tga"; + public const string Gray8BitRle = "Tga/targa_8bit_rle.tga"; + public const string Bit15 = "Tga/rgb15.tga"; public const string Bit15Rle = "Tga/rgb15rle.tga"; - public const string Bit16 = "Tga/targa_16bit.tga"; + public const string Bit16BottomLeft = "Tga/targa_16bit.tga"; public const string Bit16PalRle = "Tga/ccm8.tga"; - public const string Bit24 = "Tga/targa_24bit.tga"; - public const string Bit24TopRight = "Tga/rgb_UR.tga"; + + public const string Gray16Bit = "Tga/grayscale_a_UL.tga"; + public const string Gray16BitBottomLeft = "Tga/grayscale_a_LL.tga"; + public const string Gray16BitBottomRight = "Tga/grayscale_a_LR.tga"; + public const string Gray16BitTopRight = "Tga/grayscale_a_UR.tga"; + public const string Gray16BitRle = "Tga/grayscale_a_rle_UL.tga"; + public const string Gray16BitRleBottomLeft = "Tga/grayscale_a_rle_LL.tga"; + public const string Gray16BitRleBottomRight = "Tga/grayscale_a_rle_LR.tga"; + public const string Gray16BitRleTopRight = "Tga/grayscale_a_rle_UR.tga"; + + public const string Bit24 = "Tga/rgb24_top_left.tga"; + public const string Bit24BottomLeft = "Tga/targa_24bit.tga"; public const string Bit24BottomRight = "Tga/rgb_LR.tga"; + public const string Bit24TopRight = "Tga/rgb_UR.tga"; public const string Bit24TopLeft = "Tga/targa_24bit_pal_origin_topleft.tga"; + public const string Bit24RleTopLeft = "Tga/targa_24bit_rle_origin_topleft.tga"; public const string Bit24RleTopRight = "Tga/rgb_rle_UR.tga"; public const string Bit24RleBottomRight = "Tga/rgb_rle_LR.tga"; + public const string Bit32 = "Tga/targa_32bit.tga"; + public const string Bit32BottomLeft = "Tga/rgb_a_LL.tga"; + public const string Bit32TopRight = "Tga/rgb_a_UR.tga"; + public const string Bit32BottomRight = "Tga/rgb_a_LR.tga"; + public const string Bit32Pal = "Tga/indexed_a_UL.tga"; public const string Bit32PalBottomLeft = "Tga/indexed_a_LL.tga"; public const string Bit32PalBottomRight = "Tga/indexed_a_LR.tga"; public const string Bit32PalTopRight = "Tga/indexed_a_UR.tga"; - public const string Bit32TopRight = "Tga/rgb_a_UR.tga"; - public const string Bit32BottomRight = "Tga/rgb_a_LR.tga"; public const string Bit32RleTopRight = "Tga/rgb_a_rle_UR.tga"; public const string Bit32RleBottomRight = "Tga/rgb_a_rle_LR.tga"; - public const string Gray8Bit = "Tga/targa_8bit.tga"; - public const string Gray8BitRle = "Tga/targa_8bit_rle.tga"; - public const string Gray16Bit = "Tga/grayscale_a_UL.tga"; - public const string Gray16BitBottomLeft = "Tga/grayscale_a_LL.tga"; - public const string Gray16BitBottomRight = "Tga/grayscale_a_LR.tga"; - public const string Gray16BitRle = "Tga/grayscale_a_rle_UL.tga"; - public const string Gray16BitRleBottomLeft = "Tga/grayscale_a_rle_LL.tga"; - public const string Gray16BitRleBottomRight = "Tga/grayscale_a_rle_LR.tga"; - public const string Gray16BitRleTopRight = "Tga/grayscale_a_rle_UR.tga"; + public const string Bit16Rle = "Tga/targa_16bit_rle.tga"; public const string Bit24Rle = "Tga/targa_24bit_rle.tga"; public const string Bit32Rle = "Tga/targa_32bit_rle.tga"; public const string Bit16Pal = "Tga/targa_16bit_pal.tga"; public const string Bit24Pal = "Tga/targa_24bit_pal.tga"; + public const string NoAlphaBits16Bit = "Tga/16bit_noalphabits.tga"; public const string NoAlphaBits16BitRle = "Tga/16bit_rle_noalphabits.tga"; public const string NoAlphaBits32Bit = "Tga/32bit_no_alphabits.tga"; diff --git a/tests/Images/External b/tests/Images/External index 6f81d7a95..fe694a393 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 6f81d7a95e285b1449efd4319a5cc7b071dec679 +Subproject commit fe694a3938bea3565071a96cb1c90c4cbc586ff9 diff --git a/tests/Images/Input/Tga/grayscale_LL.tga b/tests/Images/Input/Tga/grayscale_LL.tga new file mode 100644 index 000000000..13ae52c37 --- /dev/null +++ b/tests/Images/Input/Tga/grayscale_LL.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:74ef200d90078b5cd8ff6ddf714e0a082fc420684e2d7667fe158c5705b91946 +size 65580 diff --git a/tests/Images/Input/Tga/grayscale_UR.tga b/tests/Images/Input/Tga/grayscale_UR.tga new file mode 100644 index 000000000..a33d3aa2e --- /dev/null +++ b/tests/Images/Input/Tga/grayscale_UR.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8831036fdb79dbc9fa9d6940c6bb4bfc546b83f9caf55a65853e9a60639edece +size 65580 diff --git a/tests/Images/Input/Tga/grayscale_a_UR.tga b/tests/Images/Input/Tga/grayscale_a_UR.tga new file mode 100644 index 000000000..ce2bf4dc8 --- /dev/null +++ b/tests/Images/Input/Tga/grayscale_a_UR.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ff8cdd9cf4aa48f0df2d920483aeead476166e0e958d07aa5b8a3cd2babfd834 +size 131116 diff --git a/tests/Images/Input/Tga/rgb24_top_left.tga b/tests/Images/Input/Tga/rgb24_top_left.tga new file mode 100644 index 000000000..bfaeae686 --- /dev/null +++ b/tests/Images/Input/Tga/rgb24_top_left.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f9c0aed8fb8c4e336fb1b9a6b76c9ba3e81554469191293e0b07d6afc8d9086a +size 12332 diff --git a/tests/Images/Input/Tga/rgb_a_LL.tga b/tests/Images/Input/Tga/rgb_a_LL.tga new file mode 100644 index 000000000..786eb7b7d --- /dev/null +++ b/tests/Images/Input/Tga/rgb_a_LL.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eff46c35b08b02759b5e5cf4ba473b7714cf303e35cd93ae1404b8e3277014a1 +size 262188 From b44640033b467093461babee48c643c43d74d7aa Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 30 Mar 2020 18:51:16 +0200 Subject: [PATCH 46/58] Move calculation of x and y out of the for loops where possible --- src/ImageSharp/Formats/Tga/TgaDecoderCore.cs | 326 +++++++++++++----- .../Formats/Tga/TgaDecoderTests.cs | 134 +++++-- .../Formats/Tga/TgaEncoderTests.cs | 22 +- tests/ImageSharp.Tests/TestImages.cs | 43 ++- tests/Images/Input/Tga/grayscale_LR.tga | 3 + tests/Images/Input/Tga/grayscale_UL.tga | 3 + tests/Images/Input/Tga/grayscale_rle_LR.tga | 3 + tests/Images/Input/Tga/grayscale_rle_UL.tga | 3 + tests/Images/Input/Tga/grayscale_rle_UR.tga | 3 + tests/Images/Input/Tga/indexed_LR.tga | 3 + tests/Images/Input/Tga/indexed_UL.tga | 3 + tests/Images/Input/Tga/indexed_UR.tga | 3 + tests/Images/Input/Tga/rgb_a_UL.tga | 3 + tests/Images/Input/Tga/rgb_a_rle_UL.tga | 3 + 14 files changed, 412 insertions(+), 143 deletions(-) create mode 100644 tests/Images/Input/Tga/grayscale_LR.tga create mode 100644 tests/Images/Input/Tga/grayscale_UL.tga create mode 100644 tests/Images/Input/Tga/grayscale_rle_LR.tga create mode 100644 tests/Images/Input/Tga/grayscale_rle_UL.tga create mode 100644 tests/Images/Input/Tga/grayscale_rle_UR.tga create mode 100644 tests/Images/Input/Tga/indexed_LR.tga create mode 100644 tests/Images/Input/Tga/indexed_UL.tga create mode 100644 tests/Images/Input/Tga/indexed_UR.tga create mode 100644 tests/Images/Input/Tga/rgb_a_UL.tga create mode 100644 tests/Images/Input/Tga/rgb_a_rle_UL.tga diff --git a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs index e9e7ce719..a4e9bdcd7 100644 --- a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs +++ b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs @@ -237,59 +237,69 @@ namespace SixLabors.ImageSharp.Formats.Tga private void ReadPaletted(int width, int height, Buffer2D pixels, byte[] palette, int colorMapPixelSizeInBytes, TgaImageOrigin origin) where TPixel : unmanaged, IPixel { - using (IManagedByteBuffer row = this.memoryAllocator.AllocateManagedByteBuffer(width, AllocationOptions.Clean)) + TPixel color = default; + bool invertX = InvertX(origin); + + for (int y = 0; y < height; y++) { - TPixel color = default; - Span rowSpan = row.GetSpan(); + int newY = InvertY(y, height, origin); + Span pixelRow = pixels.GetRowSpan(newY); - for (int y = 0; y < height; y++) + switch (colorMapPixelSizeInBytes) { - this.currentStream.Read(row); - int newY = InvertY(y, height, origin); - Span pixelRow = pixels.GetRowSpan(newY); - switch (colorMapPixelSizeInBytes) - { - case 2: + case 2: + if (invertX) + { + for (int x = width - 1; x >= 0; x--) + { + this.ReadPalettedBgr16Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow); + } + } + else + { for (int x = 0; x < width; x++) { - int colorIndex = rowSpan[x]; - - Bgra5551 bgra = Unsafe.As(ref palette[colorIndex * colorMapPixelSizeInBytes]); - if (!this.hasAlpha) - { - // Set alpha value to 1, to treat it as opaque for Bgra5551. - bgra.PackedValue = (ushort)(bgra.PackedValue | 0x8000); - } - - color.FromBgra5551(bgra); - int newX = InvertX(x, width, origin); - pixelRow[newX] = color; + this.ReadPalettedBgr16Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow); } + } - break; + break; - case 3: + case 3: + if (invertX) + { + for (int x = width - 1; x >= 0; x--) + { + this.ReadPalettedBgr24Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow); + } + } + else + { for (int x = 0; x < width; x++) { - int colorIndex = rowSpan[x]; - color.FromBgr24(Unsafe.As(ref palette[colorIndex * colorMapPixelSizeInBytes])); - int newX = InvertX(x, width, origin); - pixelRow[newX] = color; + this.ReadPalettedBgr24Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow); } + } - break; + break; - case 4: + case 4: + if (invertX) + { + for (int x = width - 1; x >= 0; x--) + { + this.ReadPalettedBgra32Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow); + } + } + else + { for (int x = 0; x < width; x++) { - int colorIndex = rowSpan[x]; - color.FromBgra32(Unsafe.As(ref palette[colorIndex * colorMapPixelSizeInBytes])); - int newX = InvertX(x, width, origin); - pixelRow[newX] = color; + this.ReadPalettedBgra32Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow); } + } - break; - } + break; } } } @@ -374,20 +384,17 @@ namespace SixLabors.ImageSharp.Formats.Tga private void ReadMonoChrome(int width, int height, Buffer2D pixels, TgaImageOrigin origin) where TPixel : unmanaged, IPixel { - bool isXInverted = origin == TgaImageOrigin.BottomRight || origin == TgaImageOrigin.TopRight; - if (isXInverted) + bool invertX = InvertX(origin); + if (invertX) { TPixel color = default; for (int y = 0; y < height; y++) { int newY = InvertY(y, height, origin); Span pixelSpan = pixels.GetRowSpan(newY); - for (int x = 0; x < width; x++) + for (int x = width - 1; x >= 0; x--) { - var pixelValue = (byte)this.currentStream.ReadByte(); - color.FromL8(Unsafe.As(ref pixelValue)); - int newX = InvertX(x, width, origin); - pixelSpan[newX] = color; + this.ReadL8Pixel(color, x, pixelSpan); } } @@ -396,12 +403,20 @@ namespace SixLabors.ImageSharp.Formats.Tga using (IManagedByteBuffer row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 1, 0)) { - for (int y = 0; y < height; y++) + bool invertY = InvertY(origin); + if (invertY) { - this.currentStream.Read(row); - int newY = InvertY(y, height, origin); - Span pixelSpan = pixels.GetRowSpan(newY); - PixelOperations.Instance.FromL8Bytes(this.configuration, row.GetSpan(), pixelSpan, width); + for (int y = height - 1; y >= 0; y--) + { + this.ReadL8Row(width, pixels, row, y); + } + } + else + { + for (int y = 0; y < height; y++) + { + this.ReadL8Row(width, pixels, row, y); + } } } } @@ -418,7 +433,7 @@ namespace SixLabors.ImageSharp.Formats.Tga where TPixel : unmanaged, IPixel { TPixel color = default; - bool isXInverted = origin == TgaImageOrigin.BottomRight || origin == TgaImageOrigin.TopRight; + bool invertX = InvertX(origin); using (IManagedByteBuffer row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 2, 0)) { for (int y = 0; y < height; y++) @@ -426,9 +441,9 @@ namespace SixLabors.ImageSharp.Formats.Tga int newY = InvertY(y, height, origin); Span pixelSpan = pixels.GetRowSpan(newY); - if (isXInverted) + if (invertX) { - for (int x = 0; x < width; x++) + for (int x = width - 1; x >= 0; x--) { this.currentStream.Read(this.scratchBuffer, 0, 2); if (!this.hasAlpha) @@ -445,8 +460,7 @@ namespace SixLabors.ImageSharp.Formats.Tga color.FromBgra5551(Unsafe.As(ref this.scratchBuffer[0])); } - int newX = InvertX(x, width, origin); - pixelSpan[newX] = color; + pixelSpan[x] = color; } } else @@ -487,20 +501,17 @@ namespace SixLabors.ImageSharp.Formats.Tga private void ReadBgr24(int width, int height, Buffer2D pixels, TgaImageOrigin origin) where TPixel : unmanaged, IPixel { - bool isXInverted = origin == TgaImageOrigin.BottomRight || origin == TgaImageOrigin.TopRight; - if (isXInverted) + bool invertX = InvertX(origin); + if (invertX) { TPixel color = default; for (int y = 0; y < height; y++) { int newY = InvertY(y, height, origin); Span pixelSpan = pixels.GetRowSpan(newY); - for (int x = 0; x < width; x++) + for (int x = width - 1; x >= 0; x--) { - this.currentStream.Read(this.scratchBuffer, 0, 3); - color.FromBgr24(Unsafe.As(ref this.scratchBuffer[0])); - int newX = InvertX(x, width, origin); - pixelSpan[newX] = color; + this.ReadBgr24Pixel(color, x, pixelSpan); } } @@ -509,12 +520,21 @@ namespace SixLabors.ImageSharp.Formats.Tga using (IManagedByteBuffer row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 3, 0)) { - for (int y = 0; y < height; y++) + bool invertY = InvertY(origin); + + if (invertY) { - this.currentStream.Read(row); - int newY = InvertY(y, height, origin); - Span pixelSpan = pixels.GetRowSpan(newY); - PixelOperations.Instance.FromBgr24Bytes(this.configuration, row.GetSpan(), pixelSpan, width); + for (int y = height - 1; y >= 0; y--) + { + this.ReadBgr24Row(width, pixels, row, y); + } + } + else + { + for (int y = 0; y < height; y++) + { + this.ReadBgr24Row(width, pixels, row, y); + } } } } @@ -531,41 +551,46 @@ namespace SixLabors.ImageSharp.Formats.Tga where TPixel : unmanaged, IPixel { TPixel color = default; - bool isXInverted = origin == TgaImageOrigin.BottomRight || origin == TgaImageOrigin.TopRight; - if (this.tgaMetadata.AlphaChannelBits == 8 && !isXInverted) + bool invertX = InvertX(origin); + if (this.tgaMetadata.AlphaChannelBits == 8 && !invertX) { using (IManagedByteBuffer row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 4, 0)) { - for (int y = 0; y < height; y++) + if (InvertY(origin)) { - this.currentStream.Read(row); - int newY = InvertY(y, height, origin); - Span pixelSpan = pixels.GetRowSpan(newY); - - PixelOperations.Instance.FromBgra32Bytes(this.configuration, row.GetSpan(), pixelSpan, width); + for (int y = height - 1; y >= 0; y--) + { + this.ReadBgra32Row(width, pixels, row, y); + } + } + else + { + for (int y = 0; y < height; y++) + { + this.ReadBgra32Row(width, pixels, row, y); + } } } return; } - var alphaBits = this.tgaMetadata.AlphaChannelBits; - using (IManagedByteBuffer row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 4, 0)) + for (int y = 0; y < height; y++) { - for (int y = 0; y < height; y++) + int newY = InvertY(y, height, origin); + Span pixelRow = pixels.GetRowSpan(newY); + if (invertX) + { + for (int x = width - 1; x >= 0; x--) + { + this.ReadBgra32Pixel(x, color, pixelRow); + } + } + else { - this.currentStream.Read(row); - int newY = InvertY(y, height, origin); - Span pixelRow = pixels.GetRowSpan(newY); - Span rowSpan = row.GetSpan(); - for (int x = 0; x < width; x++) { - int idx = x * 4; - var alpha = alphaBits == 0 ? byte.MaxValue : rowSpan[idx + 3]; - color.FromBgra32(new Bgra32(rowSpan[idx + 2], rowSpan[idx + 1], rowSpan[idx], (byte)alpha)); - int newX = InvertX(x, width, origin); - pixelRow[newX] = color; + this.ReadBgra32Pixel(x, color, pixelRow); } } } @@ -657,6 +682,95 @@ namespace SixLabors.ImageSharp.Formats.Tga this.metadata); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ReadL8Row(int width, Buffer2D pixels, IManagedByteBuffer row, int y) + where TPixel : unmanaged, IPixel + { + this.currentStream.Read(row); + Span pixelSpan = pixels.GetRowSpan(y); + PixelOperations.Instance.FromL8Bytes(this.configuration, row.GetSpan(), pixelSpan, width); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ReadL8Pixel(TPixel color, int x, Span pixelSpan) + where TPixel : unmanaged, IPixel + { + var pixelValue = (byte)this.currentStream.ReadByte(); + color.FromL8(Unsafe.As(ref pixelValue)); + pixelSpan[x] = color; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ReadBgr24Pixel(TPixel color, int x, Span pixelSpan) + where TPixel : unmanaged, IPixel + { + this.currentStream.Read(this.scratchBuffer, 0, 3); + color.FromBgr24(Unsafe.As(ref this.scratchBuffer[0])); + pixelSpan[x] = color; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ReadBgr24Row(int width, Buffer2D pixels, IManagedByteBuffer row, int y) + where TPixel : unmanaged, IPixel + { + this.currentStream.Read(row); + Span pixelSpan = pixels.GetRowSpan(y); + PixelOperations.Instance.FromBgr24Bytes(this.configuration, row.GetSpan(), pixelSpan, width); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ReadBgra32Pixel(int x, TPixel color, Span pixelRow) + where TPixel : unmanaged, IPixel + { + this.currentStream.Read(this.scratchBuffer, 0, 4); + var alpha = this.tgaMetadata.AlphaChannelBits == 0 ? byte.MaxValue : this.scratchBuffer[3]; + color.FromBgra32(new Bgra32(this.scratchBuffer[2], this.scratchBuffer[1], this.scratchBuffer[0], alpha)); + pixelRow[x] = color; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ReadBgra32Row(int width, Buffer2D pixels, IManagedByteBuffer row, int y) + where TPixel : unmanaged, IPixel + { + this.currentStream.Read(row); + Span pixelSpan = pixels.GetRowSpan(y); + PixelOperations.Instance.FromBgra32Bytes(this.configuration, row.GetSpan(), pixelSpan, width); + } + + private void ReadPalettedBgr16Pixel(byte[] palette, int colorMapPixelSizeInBytes, int x, TPixel color, Span pixelRow) + where TPixel : unmanaged, IPixel + { + int colorIndex = this.currentStream.ReadByte(); + Bgra5551 bgra = default; + bgra.FromBgra5551(Unsafe.As(ref palette[colorIndex * colorMapPixelSizeInBytes])); + if (!this.hasAlpha) + { + // Set alpha value to 1, to treat it as opaque for Bgra5551. + bgra.PackedValue = (ushort)(bgra.PackedValue | 0x8000); + } + + color.FromBgra5551(bgra); + pixelRow[x] = color; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ReadPalettedBgr24Pixel(byte[] palette, int colorMapPixelSizeInBytes, int x, TPixel color, Span pixelRow) + where TPixel : unmanaged, IPixel + { + int colorIndex = this.currentStream.ReadByte(); + color.FromBgr24(Unsafe.As(ref palette[colorIndex * colorMapPixelSizeInBytes])); + pixelRow[x] = color; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ReadPalettedBgra32Pixel(byte[] palette, int colorMapPixelSizeInBytes, int x, TPixel color, Span pixelRow) + where TPixel : unmanaged, IPixel + { + int colorIndex = this.currentStream.ReadByte(); + color.FromBgra32(Unsafe.As(ref palette[colorIndex * colorMapPixelSizeInBytes])); + pixelRow[x] = color; + } + /// /// Produce uncompressed tga data from a run length encoded stream. /// @@ -710,14 +824,30 @@ namespace SixLabors.ImageSharp.Formats.Tga /// The representing the inverted value. [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int InvertY(int y, int height, TgaImageOrigin origin) + { + if (InvertY(origin)) + { + return height - y - 1; + } + + return y; + } + + /// + /// Indicates whether the y coordinates needs to be inverted, to keep a top left origin. + /// + /// The image origin. + /// True, if y coordinate needs to be inverted. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool InvertY(TgaImageOrigin origin) { switch (origin) { case TgaImageOrigin.BottomLeft: case TgaImageOrigin.BottomRight: - return height - y - 1; + return true; default: - return y; + return false; } } @@ -730,14 +860,30 @@ namespace SixLabors.ImageSharp.Formats.Tga /// The representing the inverted value. [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int InvertX(int x, int width, TgaImageOrigin origin) + { + if (InvertX(origin)) + { + return width - x - 1; + } + + return x; + } + + /// + /// Indicates whether the x coordinates needs to be inverted, to keep a top left origin. + /// + /// The image origin. + /// True, if x coordinate needs to be inverted. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool InvertX(TgaImageOrigin origin) { switch (origin) { case TgaImageOrigin.TopRight: case TgaImageOrigin.BottomRight: - return width - x - 1; + return true; default: - return x; + return false; } } diff --git a/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs index ccd00b49f..f932f994d 100644 --- a/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs @@ -20,8 +20,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga private static TgaDecoder TgaDecoder => new TgaDecoder(); [Theory] - [WithFile(Gray8Bit, PixelTypes.Rgba32)] - public void TgaDecoder_CanDecode_Gray_8Bit(TestImageProvider provider) + [WithFile(Gray8BitTopLeft, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_Gray_WithTopLeftOrigin_8Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { using (Image image = provider.GetImage(TgaDecoder)) @@ -68,8 +68,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } [Theory] - [WithFile(Gray8BitRle, PixelTypes.Rgba32)] - public void TgaDecoder_CanDecode_RunLengthEncoded_Gray_8Bit(TestImageProvider provider) + [WithFile(Gray8BitRleTopLeft, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_RunLengthEncoded_Gray_WithTopLeftOrigin_8Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { using (Image image = provider.GetImage(TgaDecoder)) @@ -80,7 +80,43 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } [Theory] - [WithFile(Gray16Bit, PixelTypes.Rgba32)] + [WithFile(Gray8BitRleTopRight, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_RunLengthEncoded_Gray_WithTopRightOrigin_8Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Gray8BitRleBottomLeft, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_RunLengthEncoded_Gray_WithBottomLeftOrigin_8Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Gray8BitRleBottomRight, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_RunLengthEncoded_Gray_WithBottomRightOrigin_8Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Gray16BitTopLeft, PixelTypes.Rgba32)] public void TgaDecoder_CanDecode_Gray_16Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { @@ -140,7 +176,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } [Theory] - [WithFile(Gray16BitRle, PixelTypes.Rgba32)] + [WithFile(Gray16BitRleTopLeft, PixelTypes.Rgba32)] public void TgaDecoder_CanDecode_RunLengthEncoded_Gray_16Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { @@ -248,8 +284,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } [Theory] - [WithFile(Bit24, PixelTypes.Rgba32)] - public void TgaDecoder_CanDecode_24Bit(TestImageProvider provider) + [WithFile(Bit24TopLeft, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_WithTopLeftOrigin_24Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { using (Image image = provider.GetImage(TgaDecoder)) @@ -344,8 +380,20 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } [Theory] - [WithFile(Bit32, PixelTypes.Rgba32)] - public void TgaDecoder_CanDecode_32Bit(TestImageProvider provider) + [WithFile(Bit32TopLeft, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_WithTopLeftOrigin_32Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Bit32TopRight, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_WithTopRightOrigin_32Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { using (Image image = provider.GetImage(TgaDecoder)) @@ -380,8 +428,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } [Theory] - [WithFile(Bit32TopRight, PixelTypes.Rgba32)] - public void TgaDecoder_CanDecode_WithTopRightOrigin_32Bit(TestImageProvider provider) + [WithFile(Bit16RleBottomLeft, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_RunLengthEncoded_WithBottomLeftOrigin_16Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { using (Image image = provider.GetImage(TgaDecoder)) @@ -392,8 +440,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } [Theory] - [WithFile(Bit16Rle, PixelTypes.Rgba32)] - public void TgaDecoder_CanDecode_RunLengthEncoded_16Bit(TestImageProvider provider) + [WithFile(Bit24RleBottomLeft, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_RunLengthEncoded_WithBottomLeftOrigin_24Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { using (Image image = provider.GetImage(TgaDecoder)) @@ -404,8 +452,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } [Theory] - [WithFile(Bit24Rle, PixelTypes.Rgba32)] - public void TgaDecoder_CanDecode_RunLengthEncoded_24Bit(TestImageProvider provider) + [WithFile(Bit32RleTopLeft, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_RunLengthEncoded_WithTopLeftOrigin_32Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { using (Image image = provider.GetImage(TgaDecoder)) @@ -416,8 +464,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } [Theory] - [WithFile(Bit32Rle, PixelTypes.Rgba32)] - public void TgaDecoder_CanDecode_RunLengthEncoded_32Bit(TestImageProvider provider) + [WithFile(Bit32RleBottomLeft, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_RunLengthEncoded_WithBottomLeftOrigin_32Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { using (Image image = provider.GetImage(TgaDecoder)) @@ -452,8 +500,32 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } [Theory] - [WithFile(Bit16Pal, PixelTypes.Rgba32)] - public void TgaDecoder_CanDecode_WithPalette_16Bit(TestImageProvider provider) + [WithFile(Bit16PalBottomLeft, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_WithPaletteBottomLeftOrigin_16Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Bit24PalTopLeft, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_WithPaletteTopLeftOrigin_24Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Bit24PalTopRight, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_WithPaletteTopRightOrigin_24Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { using (Image image = provider.GetImage(TgaDecoder)) @@ -464,8 +536,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } [Theory] - [WithFile(Bit24Pal, PixelTypes.Rgba32)] - public void TgaDecoder_CanDecode_WithPalette_24Bit(TestImageProvider provider) + [WithFile(Bit24PalBottomLeft, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_WithPaletteBottomLeftOrigin_24Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { using (Image image = provider.GetImage(TgaDecoder)) @@ -476,7 +548,19 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } [Theory] - [WithFile(Bit32Pal, PixelTypes.Rgba32)] + [WithFile(Bit24PalBottomRight, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_WithPaletteBottomRightOrigin_24Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Bit32PalTopLeft, PixelTypes.Rgba32)] public void TgaDecoder_CanDecode_WithPalette_32Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { @@ -554,7 +638,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga [Theory] [WithFile(Bit16BottomLeft, PixelTypes.Rgba32)] [WithFile(Bit24BottomLeft, PixelTypes.Rgba32)] - [WithFile(Bit32, PixelTypes.Rgba32)] + [WithFile(Bit32BottomLeft, PixelTypes.Rgba32)] public void TgaDecoder_DegenerateMemoryRequest_ShouldTranslateTo_ImageFormatException(TestImageProvider provider) where TPixel : unmanaged, IPixel { @@ -565,7 +649,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga [Theory] [WithFile(Bit24BottomLeft, PixelTypes.Rgba32)] - [WithFile(Bit32, PixelTypes.Rgba32)] + [WithFile(Bit32BottomLeft, PixelTypes.Rgba32)] public void TgaDecoder_CanDecode_WithLimitedAllocatorBufferCapacity(TestImageProvider provider) where TPixel : unmanaged, IPixel { diff --git a/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs index 4e3e2d24c..6e0fa4a0e 100644 --- a/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs @@ -25,10 +25,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga public static readonly TheoryData TgaBitsPerPixelFiles = new TheoryData { - { Gray8Bit, TgaBitsPerPixel.Pixel8 }, + { Gray8BitBottomLeft, TgaBitsPerPixel.Pixel8 }, { Bit16BottomLeft, TgaBitsPerPixel.Pixel16 }, { Bit24BottomLeft, TgaBitsPerPixel.Pixel24 }, - { Bit32, TgaBitsPerPixel.Pixel32 }, + { Bit32BottomLeft, TgaBitsPerPixel.Pixel32 }, }; [Theory] @@ -79,51 +79,51 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } [Theory] - [WithFile(Bit32, PixelTypes.Rgba32)] + [WithFile(Bit32BottomLeft, PixelTypes.Rgba32)] public void TgaEncoder_Bit8_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel8) // Using tolerant comparer here. The results from magick differ slightly. Maybe a different ToGrey method is used. The image looks otherwise ok. where TPixel : unmanaged, IPixel => TestTgaEncoderCore(provider, bitsPerPixel, TgaCompression.None, useExactComparer: false, compareTolerance: 0.03f); [Theory] - [WithFile(Bit32, PixelTypes.Rgba32)] + [WithFile(Bit32BottomLeft, PixelTypes.Rgba32)] public void TgaEncoder_Bit16_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel16) where TPixel : unmanaged, IPixel => TestTgaEncoderCore(provider, bitsPerPixel, TgaCompression.None, useExactComparer: false); [Theory] - [WithFile(Bit32, PixelTypes.Rgba32)] + [WithFile(Bit32BottomLeft, PixelTypes.Rgba32)] public void TgaEncoder_Bit24_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel24) where TPixel : unmanaged, IPixel => TestTgaEncoderCore(provider, bitsPerPixel, TgaCompression.None); [Theory] - [WithFile(Bit32, PixelTypes.Rgba32)] + [WithFile(Bit32BottomLeft, PixelTypes.Rgba32)] public void TgaEncoder_Bit32_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel32) where TPixel : unmanaged, IPixel => TestTgaEncoderCore(provider, bitsPerPixel, TgaCompression.None); [Theory] - [WithFile(Bit32, PixelTypes.Rgba32)] + [WithFile(Bit32BottomLeft, PixelTypes.Rgba32)] public void TgaEncoder_Bit8_WithRunLengthEncoding_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel8) // Using tolerant comparer here. The results from magick differ slightly. Maybe a different ToGrey method is used. The image looks otherwise ok. where TPixel : unmanaged, IPixel => TestTgaEncoderCore(provider, bitsPerPixel, TgaCompression.RunLength, useExactComparer: false, compareTolerance: 0.03f); [Theory] - [WithFile(Bit32, PixelTypes.Rgba32)] + [WithFile(Bit32BottomLeft, PixelTypes.Rgba32)] public void TgaEncoder_Bit16_WithRunLengthEncoding_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel16) where TPixel : unmanaged, IPixel => TestTgaEncoderCore(provider, bitsPerPixel, TgaCompression.RunLength, useExactComparer: false); [Theory] - [WithFile(Bit32, PixelTypes.Rgba32)] + [WithFile(Bit32BottomLeft, PixelTypes.Rgba32)] public void TgaEncoder_Bit24_WithRunLengthEncoding_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel24) where TPixel : unmanaged, IPixel => TestTgaEncoderCore(provider, bitsPerPixel, TgaCompression.RunLength); [Theory] - [WithFile(Bit32, PixelTypes.Rgba32)] + [WithFile(Bit32BottomLeft, PixelTypes.Rgba32)] public void TgaEncoder_Bit32_WithRunLengthEncoding_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel32) where TPixel : unmanaged, IPixel => TestTgaEncoderCore(provider, bitsPerPixel, TgaCompression.RunLength); [Theory] - [WithFile(Bit32, PixelTypes.Rgba32, TgaBitsPerPixel.Pixel32)] + [WithFile(Bit32BottomLeft, PixelTypes.Rgba32, TgaBitsPerPixel.Pixel32)] [WithFile(Bit24BottomLeft, PixelTypes.Rgba32, TgaBitsPerPixel.Pixel24)] public void TgaEncoder_WorksWithDiscontiguousBuffers(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel) where TPixel : unmanaged, IPixel diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 38ef8d9e2..272998a89 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -375,53 +375,62 @@ namespace SixLabors.ImageSharp.Tests public static class Tga { - public const string Gray8Bit = "Tga/targa_8bit.tga"; - public const string Gray8BitBottomLeft = "Tga/grayscale_LL.tga"; + public const string Gray8BitTopLeft = "Tga/grayscale_UL.tga"; public const string Gray8BitTopRight = "Tga/grayscale_UR.tga"; - public const string Gray8BitBottomRight = "Tga/grayscale_UR.tga"; - public const string Gray8BitRle = "Tga/targa_8bit_rle.tga"; + public const string Gray8BitBottomLeft = "Tga/targa_8bit.tga"; + public const string Gray8BitBottomRight = "Tga/grayscale_LR.tga"; + + public const string Gray8BitRleTopLeft = "Tga/grayscale_rle_UL.tga"; + public const string Gray8BitRleTopRight = "Tga/grayscale_rle_UR.tga"; + public const string Gray8BitRleBottomLeft = "Tga/targa_8bit_rle.tga"; + public const string Gray8BitRleBottomRight = "Tga/grayscale_rle_LR.tga"; public const string Bit15 = "Tga/rgb15.tga"; public const string Bit15Rle = "Tga/rgb15rle.tga"; public const string Bit16BottomLeft = "Tga/targa_16bit.tga"; public const string Bit16PalRle = "Tga/ccm8.tga"; + public const string Bit16RleBottomLeft = "Tga/targa_16bit_rle.tga"; + public const string Bit16PalBottomLeft = "Tga/targa_16bit_pal.tga"; - public const string Gray16Bit = "Tga/grayscale_a_UL.tga"; + public const string Gray16BitTopLeft = "Tga/grayscale_a_UL.tga"; public const string Gray16BitBottomLeft = "Tga/grayscale_a_LL.tga"; public const string Gray16BitBottomRight = "Tga/grayscale_a_LR.tga"; public const string Gray16BitTopRight = "Tga/grayscale_a_UR.tga"; - public const string Gray16BitRle = "Tga/grayscale_a_rle_UL.tga"; + + public const string Gray16BitRleTopLeft = "Tga/grayscale_a_rle_UL.tga"; public const string Gray16BitRleBottomLeft = "Tga/grayscale_a_rle_LL.tga"; public const string Gray16BitRleBottomRight = "Tga/grayscale_a_rle_LR.tga"; public const string Gray16BitRleTopRight = "Tga/grayscale_a_rle_UR.tga"; - public const string Bit24 = "Tga/rgb24_top_left.tga"; + public const string Bit24TopLeft = "Tga/rgb24_top_left.tga"; public const string Bit24BottomLeft = "Tga/targa_24bit.tga"; public const string Bit24BottomRight = "Tga/rgb_LR.tga"; public const string Bit24TopRight = "Tga/rgb_UR.tga"; - public const string Bit24TopLeft = "Tga/targa_24bit_pal_origin_topleft.tga"; public const string Bit24RleTopLeft = "Tga/targa_24bit_rle_origin_topleft.tga"; + public const string Bit24RleBottomLeft = "Tga/targa_24bit_rle.tga"; public const string Bit24RleTopRight = "Tga/rgb_rle_UR.tga"; public const string Bit24RleBottomRight = "Tga/rgb_rle_LR.tga"; - public const string Bit32 = "Tga/targa_32bit.tga"; - public const string Bit32BottomLeft = "Tga/rgb_a_LL.tga"; + public const string Bit24PalTopLeft = "Tga/targa_24bit_pal_origin_topleft.tga"; + public const string Bit24PalTopRight = "Tga/indexed_UR.tga"; + public const string Bit24PalBottomLeft = "Tga/targa_24bit_pal.tga"; + public const string Bit24PalBottomRight = "Tga/indexed_LR.tga"; + + public const string Bit32TopLeft = "Tga/rgb_a_UL.tga"; + public const string Bit32BottomLeft = "Tga/targa_32bit.tga"; public const string Bit32TopRight = "Tga/rgb_a_UR.tga"; public const string Bit32BottomRight = "Tga/rgb_a_LR.tga"; - public const string Bit32Pal = "Tga/indexed_a_UL.tga"; + public const string Bit32PalTopLeft = "Tga/indexed_a_UL.tga"; public const string Bit32PalBottomLeft = "Tga/indexed_a_LL.tga"; public const string Bit32PalBottomRight = "Tga/indexed_a_LR.tga"; public const string Bit32PalTopRight = "Tga/indexed_a_UR.tga"; + + public const string Bit32RleTopLeft = "Tga/rgb_a_rle_UL.tga"; public const string Bit32RleTopRight = "Tga/rgb_a_rle_UR.tga"; public const string Bit32RleBottomRight = "Tga/rgb_a_rle_LR.tga"; - - public const string Bit16Rle = "Tga/targa_16bit_rle.tga"; - public const string Bit24Rle = "Tga/targa_24bit_rle.tga"; - public const string Bit32Rle = "Tga/targa_32bit_rle.tga"; - public const string Bit16Pal = "Tga/targa_16bit_pal.tga"; - public const string Bit24Pal = "Tga/targa_24bit_pal.tga"; + public const string Bit32RleBottomLeft = "Tga/targa_32bit_rle.tga"; public const string NoAlphaBits16Bit = "Tga/16bit_noalphabits.tga"; public const string NoAlphaBits16BitRle = "Tga/16bit_rle_noalphabits.tga"; diff --git a/tests/Images/Input/Tga/grayscale_LR.tga b/tests/Images/Input/Tga/grayscale_LR.tga new file mode 100644 index 000000000..01c71b81c --- /dev/null +++ b/tests/Images/Input/Tga/grayscale_LR.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ed269c8f3bb462d963188d7352ebe85ab20357ac7803e5ac4d7110a23b9e6ddb +size 65580 diff --git a/tests/Images/Input/Tga/grayscale_UL.tga b/tests/Images/Input/Tga/grayscale_UL.tga new file mode 100644 index 000000000..7670e83f1 --- /dev/null +++ b/tests/Images/Input/Tga/grayscale_UL.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:72c6e1e09b923455e0c8cd14c37b358eb578bc14a0a8fcedde3ab81769960eb7 +size 65580 diff --git a/tests/Images/Input/Tga/grayscale_rle_LR.tga b/tests/Images/Input/Tga/grayscale_rle_LR.tga new file mode 100644 index 000000000..766d3884c --- /dev/null +++ b/tests/Images/Input/Tga/grayscale_rle_LR.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a897be6870be2cd183e7678e954767fd12a763c7bfce0f2246f1b7cc1ad08804 +size 31165 diff --git a/tests/Images/Input/Tga/grayscale_rle_UL.tga b/tests/Images/Input/Tga/grayscale_rle_UL.tga new file mode 100644 index 000000000..699e7ae5b --- /dev/null +++ b/tests/Images/Input/Tga/grayscale_rle_UL.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3f11be4af2283059e869543949588fe19db0e36dec64157ad9a61711cb5e6428 +size 31198 diff --git a/tests/Images/Input/Tga/grayscale_rle_UR.tga b/tests/Images/Input/Tga/grayscale_rle_UR.tga new file mode 100644 index 000000000..c61503db8 --- /dev/null +++ b/tests/Images/Input/Tga/grayscale_rle_UR.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f5aa67ec6d3408fd469ec8e7c5613daf130be893e0b76dee2994a2c32ddae471 +size 31054 diff --git a/tests/Images/Input/Tga/indexed_LR.tga b/tests/Images/Input/Tga/indexed_LR.tga new file mode 100644 index 000000000..659c3bcea --- /dev/null +++ b/tests/Images/Input/Tga/indexed_LR.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e6d5219fadf7d8b743d35c7e16f11e1182f76351757ff962e0a27f81c357b1fb +size 66315 diff --git a/tests/Images/Input/Tga/indexed_UL.tga b/tests/Images/Input/Tga/indexed_UL.tga new file mode 100644 index 000000000..da2a3f8ef --- /dev/null +++ b/tests/Images/Input/Tga/indexed_UL.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7f42dd07528f9e4f7914a570c027cc845edfe6d3fcdfa45ec8f21bc254cc1f1f +size 66315 diff --git a/tests/Images/Input/Tga/indexed_UR.tga b/tests/Images/Input/Tga/indexed_UR.tga new file mode 100644 index 000000000..a497383ab --- /dev/null +++ b/tests/Images/Input/Tga/indexed_UR.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:90d8caa10d3a05f845f94b176a77a2ed85e25b3d460527c96abfe793870c89b8 +size 66315 diff --git a/tests/Images/Input/Tga/rgb_a_UL.tga b/tests/Images/Input/Tga/rgb_a_UL.tga new file mode 100644 index 000000000..7ee3a5212 --- /dev/null +++ b/tests/Images/Input/Tga/rgb_a_UL.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1a167af1f8d64119e206593f8944c0b7901393a1b97d703c0121b8a59cae03f4 +size 262188 diff --git a/tests/Images/Input/Tga/rgb_a_rle_UL.tga b/tests/Images/Input/Tga/rgb_a_rle_UL.tga new file mode 100644 index 000000000..0ea58fd1d --- /dev/null +++ b/tests/Images/Input/Tga/rgb_a_rle_UL.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:be1323021deead462ef38c17eea5d59aea7467ae33b91bd65b542085e74aa4e4 +size 98427 From a80c36ab3a4869b14ea056c76194f9403e19c246 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 31 Mar 2020 11:59:40 +0200 Subject: [PATCH 47/58] Fix build error due to renamed files --- tests/ImageSharp.Benchmarks/Codecs/DecodeTga.cs | 2 +- tests/ImageSharp.Benchmarks/Codecs/EncodeTga.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ImageSharp.Benchmarks/Codecs/DecodeTga.cs b/tests/ImageSharp.Benchmarks/Codecs/DecodeTga.cs index 3c8f45edb..072bd53ed 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/DecodeTga.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/DecodeTga.cs @@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs private byte[] data; - [Params(TestImages.Tga.Bit24)] + [Params(TestImages.Tga.Bit24BottomLeft)] public string TestImage { get; set; } [GlobalSetup] diff --git a/tests/ImageSharp.Benchmarks/Codecs/EncodeTga.cs b/tests/ImageSharp.Benchmarks/Codecs/EncodeTga.cs index 7100ca6b7..f10eacb28 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/EncodeTga.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/EncodeTga.cs @@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); - [Params(TestImages.Tga.Bit24)] + [Params(TestImages.Tga.Bit24BottomLeft)] public string TestImage { get; set; } [GlobalSetup] From aad3f1593b4bf7181ffcd7d8de74abd805798eef Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 31 Mar 2020 19:11:50 +0200 Subject: [PATCH 48/58] Reading paletted rle tga will not ignore alpha even if alpha bits is 0 --- src/ImageSharp/Formats/Tga/TgaDecoderCore.cs | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs index a4e9bdcd7..7753b916d 100644 --- a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs +++ b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs @@ -321,7 +321,6 @@ namespace SixLabors.ImageSharp.Formats.Tga using (IMemoryOwner buffer = this.memoryAllocator.Allocate(width * height * bytesPerPixel, AllocationOptions.Clean)) { TPixel color = default; - var alphaBits = this.tgaMetadata.AlphaChannelBits; Span bufferSpan = buffer.GetSpan(); this.UncompressRle(width, height, bufferSpan, bytesPerPixel: 1); @@ -339,30 +338,17 @@ namespace SixLabors.ImageSharp.Formats.Tga color.FromL8(Unsafe.As(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes])); break; case 2: - Bgra5551 bgra = Unsafe.As(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes]); - if (!this.hasAlpha) - { - // Set alpha value to 1, to treat it as opaque for Bgra5551. - bgra.PackedValue = (ushort)(bgra.PackedValue | 0x8000); - } + // Set alpha value to 1, to treat it as opaque for Bgra5551. + bgra.PackedValue = (ushort)(bgra.PackedValue | 0x8000); color.FromBgra5551(bgra); break; case 3: color.FromBgr24(Unsafe.As(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes])); break; case 4: - if (this.hasAlpha) - { - color.FromBgra32(Unsafe.As(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes])); - } - else - { - var alpha = alphaBits == 0 ? byte.MaxValue : bufferSpan[idx + 3]; - color.FromBgra32(new Bgra32(bufferSpan[idx + 2], bufferSpan[idx + 1], bufferSpan[idx], (byte)alpha)); - } - + color.FromBgra32(Unsafe.As(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes])); break; } From 8ddd779db609c48e8319103964dc6ed96e594a73 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 31 Mar 2020 19:14:36 +0200 Subject: [PATCH 49/58] Add more tga test cases for paletted rle images --- .../Formats/Tga/TgaDecoderTests.cs | 97 +++++++++++++++++++ tests/ImageSharp.Tests/TestImages.cs | 10 ++ tests/Images/Input/Tga/indexed_a_rle_LL.tga | 3 + tests/Images/Input/Tga/indexed_a_rle_LR.tga | 3 + tests/Images/Input/Tga/indexed_a_rle_UL.tga | 3 + tests/Images/Input/Tga/indexed_a_rle_UR.tga | 3 + tests/Images/Input/Tga/indexed_rle_LL.tga | 3 + tests/Images/Input/Tga/indexed_rle_LR.tga | 3 + tests/Images/Input/Tga/indexed_rle_UL.tga | 3 + tests/Images/Input/Tga/indexed_rle_UR.tga | 3 + 10 files changed, 131 insertions(+) create mode 100644 tests/Images/Input/Tga/indexed_a_rle_LL.tga create mode 100644 tests/Images/Input/Tga/indexed_a_rle_LR.tga create mode 100644 tests/Images/Input/Tga/indexed_a_rle_UL.tga create mode 100644 tests/Images/Input/Tga/indexed_a_rle_UR.tga create mode 100644 tests/Images/Input/Tga/indexed_rle_LL.tga create mode 100644 tests/Images/Input/Tga/indexed_rle_LR.tga create mode 100644 tests/Images/Input/Tga/indexed_rle_UL.tga create mode 100644 tests/Images/Input/Tga/indexed_rle_UR.tga diff --git a/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs index f932f994d..840bb55f2 100644 --- a/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs @@ -6,6 +6,7 @@ using Microsoft.DotNet.RemoteExecutor; using SixLabors.ImageSharp.Formats.Tga; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Tests.TestUtilities; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using Xunit; @@ -499,6 +500,54 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } } + [Theory] + [WithFile(Bit32PalRleTopLeft, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_RLE_Paletted_WithTopLeftOrigin_32Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Bit32PalRleBottomLeft, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_RLE_Paletted_WithBottomLeftOrigin_32Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Bit32PalRleTopRight, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_RLE_WithTopRightOrigin_32Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Bit32PalRleBottomRight, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_RLE_Paletted_WithBottomRightOrigin_32Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + [Theory] [WithFile(Bit16PalBottomLeft, PixelTypes.Rgba32)] public void TgaDecoder_CanDecode_WithPaletteBottomLeftOrigin_16Bit(TestImageProvider provider) @@ -559,6 +608,54 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } } + [Theory] + [WithFile(Bit24PalRleTopLeft, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_RLE_WithPaletteTopLeftOrigin_24Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Bit24PalRleTopRight, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_RLE_WithPaletteTopRightOrigin_24Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Bit24PalRleBottomLeft, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_RLE_WithPaletteBottomLeftOrigin_24Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Bit24PalRleBottomRight, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_RLE_WithPaletteBottomRightOrigin_24Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + [Theory] [WithFile(Bit32PalTopLeft, PixelTypes.Rgba32)] public void TgaDecoder_CanDecode_WithPalette_32Bit(TestImageProvider provider) diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 272998a89..53a27eb31 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -417,6 +417,11 @@ namespace SixLabors.ImageSharp.Tests public const string Bit24PalBottomLeft = "Tga/targa_24bit_pal.tga"; public const string Bit24PalBottomRight = "Tga/indexed_LR.tga"; + public const string Bit24PalRleTopLeft = "Tga/indexed_rle_UL.tga"; + public const string Bit24PalRleBottomLeft = "Tga/indexed_rle_LL.tga"; + public const string Bit24PalRleTopRight = "Tga/indexed_rle_UR.tga"; + public const string Bit24PalRleBottomRight = "Tga/indexed_rle_LR.tga"; + public const string Bit32TopLeft = "Tga/rgb_a_UL.tga"; public const string Bit32BottomLeft = "Tga/targa_32bit.tga"; public const string Bit32TopRight = "Tga/rgb_a_UR.tga"; @@ -432,6 +437,11 @@ namespace SixLabors.ImageSharp.Tests public const string Bit32RleBottomRight = "Tga/rgb_a_rle_LR.tga"; public const string Bit32RleBottomLeft = "Tga/targa_32bit_rle.tga"; + public const string Bit32PalRleTopLeft = "Tga/indexed_a_rle_UL.tga"; + public const string Bit32PalRleBottomLeft = "Tga/indexed_a_rle_LL.tga"; + public const string Bit32PalRleTopRight = "Tga/indexed_a_rle_UR.tga"; + public const string Bit32PalRleBottomRight = "Tga/indexed_a_rle_LR.tga"; + public const string NoAlphaBits16Bit = "Tga/16bit_noalphabits.tga"; public const string NoAlphaBits16BitRle = "Tga/16bit_rle_noalphabits.tga"; public const string NoAlphaBits32Bit = "Tga/32bit_no_alphabits.tga"; diff --git a/tests/Images/Input/Tga/indexed_a_rle_LL.tga b/tests/Images/Input/Tga/indexed_a_rle_LL.tga new file mode 100644 index 000000000..147cc9101 --- /dev/null +++ b/tests/Images/Input/Tga/indexed_a_rle_LL.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2be79621e93dfdbd3ec9bea5085675719429cb264b1f9bbafa4ab2c9da28f677 +size 31665 diff --git a/tests/Images/Input/Tga/indexed_a_rle_LR.tga b/tests/Images/Input/Tga/indexed_a_rle_LR.tga new file mode 100644 index 000000000..6859107d0 --- /dev/null +++ b/tests/Images/Input/Tga/indexed_a_rle_LR.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:419d28012037d85794d6839fc8bdaa4b830daf8d078b536a655dc65370c15a38 +size 31776 diff --git a/tests/Images/Input/Tga/indexed_a_rle_UL.tga b/tests/Images/Input/Tga/indexed_a_rle_UL.tga new file mode 100644 index 000000000..be44253d2 --- /dev/null +++ b/tests/Images/Input/Tga/indexed_a_rle_UL.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:892b19c5e4da9ba4b96d3458d2ee35e1f64ca65e8f8f8b6eebb284e83a6bceab +size 31765 diff --git a/tests/Images/Input/Tga/indexed_a_rle_UR.tga b/tests/Images/Input/Tga/indexed_a_rle_UR.tga new file mode 100644 index 000000000..b308ff734 --- /dev/null +++ b/tests/Images/Input/Tga/indexed_a_rle_UR.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:42586d5d45bb922671755d019fe8d5f76c10ab856fcf6521fb7d114fba118c71 +size 31666 diff --git a/tests/Images/Input/Tga/indexed_rle_LL.tga b/tests/Images/Input/Tga/indexed_rle_LL.tga new file mode 100644 index 000000000..6576d515a --- /dev/null +++ b/tests/Images/Input/Tga/indexed_rle_LL.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e3dbf4ae9566e00d2165d74f3c17208853954b4c1f1cbc4ebc321fe3adb29676 +size 30549 diff --git a/tests/Images/Input/Tga/indexed_rle_LR.tga b/tests/Images/Input/Tga/indexed_rle_LR.tga new file mode 100644 index 000000000..2c14e3764 --- /dev/null +++ b/tests/Images/Input/Tga/indexed_rle_LR.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:32461dcf64ec2f6ccf6e17a7209c769a5594b8c94a31de7cc693d6abe6ba2081 +size 30610 diff --git a/tests/Images/Input/Tga/indexed_rle_UL.tga b/tests/Images/Input/Tga/indexed_rle_UL.tga new file mode 100644 index 000000000..0a06b3a86 --- /dev/null +++ b/tests/Images/Input/Tga/indexed_rle_UL.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:841f05e9f8ecdade8c992b830b9bf5893494f41accb0f0fec30f4692866c1675 +size 30640 diff --git a/tests/Images/Input/Tga/indexed_rle_UR.tga b/tests/Images/Input/Tga/indexed_rle_UR.tga new file mode 100644 index 000000000..1e68e545e --- /dev/null +++ b/tests/Images/Input/Tga/indexed_rle_UR.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a709594fd475dbe042c16671959bfbc0031e64db8e74375f01c29685d2e384ec +size 30500 From 485098fbdb0d85a94b82c91e820b56e646e6c3af Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Thu, 2 Apr 2020 12:05:51 +0200 Subject: [PATCH 50/58] Re-add external test images --- tests/Images/External | 1 + 1 file changed, 1 insertion(+) create mode 160000 tests/Images/External diff --git a/tests/Images/External b/tests/Images/External new file mode 160000 index 000000000..fe694a393 --- /dev/null +++ b/tests/Images/External @@ -0,0 +1 @@ +Subproject commit fe694a3938bea3565071a96cb1c90c4cbc586ff9 From 225df574880a5bcc1c9a2bd9996479ac63440a55 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Thu, 2 Apr 2020 12:31:35 +0200 Subject: [PATCH 51/58] Adjustments to changes from the upstream --- .../Processing/AdaptiveThresholdExtensions.cs | 49 ++--- .../AdaptiveThresholdProcessor.cs | 149 ++++------------ .../AdaptiveThresholdProcessor{TPixel}.cs | 168 ++++++++++++++++++ 3 files changed, 220 insertions(+), 146 deletions(-) create mode 100644 src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs diff --git a/src/ImageSharp/Processing/AdaptiveThresholdExtensions.cs b/src/ImageSharp/Processing/AdaptiveThresholdExtensions.cs index 33cf4b45b..4fc78a958 100644 --- a/src/ImageSharp/Processing/AdaptiveThresholdExtensions.cs +++ b/src/ImageSharp/Processing/AdaptiveThresholdExtensions.cs @@ -1,11 +1,12 @@ -using SixLabors.ImageSharp.PixelFormats; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + using SixLabors.ImageSharp.Processing.Processors.Binarization; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Extensions to perform AdaptiveThreshold through Mutator + /// Extensions to perform AdaptiveThreshold through Mutator. /// public static class AdaptiveThresholdExtensions { @@ -13,47 +14,39 @@ namespace SixLabors.ImageSharp.Processing /// Applies Bradley Adaptive Threshold to the image. /// /// The image this method extends. - /// The pixel format. /// The . - public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source) - where TPixel : struct, IPixel - => source.ApplyProcessor(new AdaptiveThresholdProcessor()); + public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source) + => source.ApplyProcessor(new AdaptiveThresholdProcessor()); /// /// Applies Bradley Adaptive Threshold to the image. /// /// The image this method extends. /// Threshold limit (0.0-1.0) to consider for binarization. - /// The pixel format. /// The . - public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, float thresholdLimit) - where TPixel : struct, IPixel - => source.ApplyProcessor(new AdaptiveThresholdProcessor(thresholdLimit)); + public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, float thresholdLimit) + => source.ApplyProcessor(new AdaptiveThresholdProcessor(thresholdLimit)); /// /// Applies Bradley Adaptive Threshold to the image. /// /// The image this method extends. /// Upper (white) color for thresholding. - /// Lower (black) color for thresholding - /// The pixel format. + /// Lower (black) color for thresholding. /// The . - public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, TPixel upper, TPixel lower) - where TPixel : struct, IPixel - => source.ApplyProcessor(new AdaptiveThresholdProcessor(upper, lower)); + public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, Color upper, Color lower) + => source.ApplyProcessor(new AdaptiveThresholdProcessor(upper, lower)); /// /// Applies Bradley Adaptive Threshold to the image. /// /// The image this method extends. /// Upper (white) color for thresholding. - /// Lower (black) color for thresholding + /// Lower (black) color for thresholding. /// Threshold limit (0.0-1.0) to consider for binarization. - /// The pixel format. /// The . - public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, TPixel upper, TPixel lower, float thresholdLimit) - where TPixel : struct, IPixel - => source.ApplyProcessor(new AdaptiveThresholdProcessor(upper, lower, thresholdLimit)); + public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, Color upper, Color lower, float thresholdLimit) + => source.ApplyProcessor(new AdaptiveThresholdProcessor(upper, lower, thresholdLimit)); /// /// Applies Bradley Adaptive Threshold to the image. @@ -62,11 +55,9 @@ namespace SixLabors.ImageSharp.Processing /// Upper (white) color for thresholding. /// Lower (black) color for thresholding /// Rectangle region to apply the processor on. - /// The pixel format. /// The . - public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, TPixel upper, TPixel lower, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new AdaptiveThresholdProcessor(upper, lower), rectangle); + public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, Color upper, Color lower, Rectangle rectangle) + => source.ApplyProcessor(new AdaptiveThresholdProcessor(upper, lower), rectangle); /// /// Applies Bradley Adaptive Threshold to the image. @@ -76,10 +67,8 @@ namespace SixLabors.ImageSharp.Processing /// Lower (black) color for thresholding /// Threshold limit (0.0-1.0) to consider for binarization. /// Rectangle region to apply the processor on. - /// The pixel format. /// The . - public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, TPixel upper, TPixel lower, float thresholdLimit, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new AdaptiveThresholdProcessor(upper, lower, thresholdLimit), rectangle); + public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, Color upper, Color lower, float thresholdLimit, Rectangle rectangle) + => source.ApplyProcessor(new AdaptiveThresholdProcessor(upper, lower, thresholdLimit), rectangle); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs index 2ad170e38..3558a9489 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs @@ -1,160 +1,77 @@ -// 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; -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.Binarization { /// - /// Performs Bradley Adaptive Threshold filter against an image + /// Performs Bradley Adaptive Threshold filter against an image. /// - /// The pixel format of the image - internal class AdaptiveThresholdProcessor : ImageProcessor - where TPixel : struct, IPixel + /// + /// Implements "Adaptive Thresholding Using the Integral Image", + /// see paper: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.420.7883&rep=rep1&type=pdf + /// + public class AdaptiveThresholdProcessor : IImageProcessor { - private readonly PixelOperations pixelOpInstance; - /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public AdaptiveThresholdProcessor() - : this(NamedColors.White, NamedColors.Black, 0.85f) + : this(Color.White, Color.Black, 0.85f) { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// Threshold limit + /// Threshold limit. public AdaptiveThresholdProcessor(float thresholdLimit) - : this(NamedColors.White, NamedColors.Black, thresholdLimit) + : this(Color.White, Color.Black, thresholdLimit) { } - public AdaptiveThresholdProcessor(TPixel upper, TPixel lower) + /// + /// Initializes a new instance of the class. + /// + /// Color for upper threshold. + /// Color for lower threshold. + public AdaptiveThresholdProcessor(Color upper, Color lower) : this(upper, lower, 0.85f) { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// Color for upper threshold - /// Color for lower threshold - /// Threshold limit - public AdaptiveThresholdProcessor(TPixel upper, TPixel lower, float thresholdLimit) + /// Color for upper threshold. + /// Color for lower threshold. + /// Threshold limit. + public AdaptiveThresholdProcessor(Color upper, Color lower, float thresholdLimit) { - this.pixelOpInstance = PixelOperations.Instance; - this.Upper = upper; this.Lower = lower; this.ThresholdLimit = thresholdLimit; } /// - /// Gets or sets upper color limit for thresholding + /// Gets or sets upper color limit for thresholding. /// - public TPixel Upper { get; set; } + public Color Upper { get; set; } /// - /// Gets or sets lower color limit for threshold + /// Gets or sets lower color limit for threshold. /// - public TPixel Lower { get; set; } + public Color Lower { get; set; } /// - /// Gets or sets the value for threshold limit + /// Gets or sets the value for threshold limit. /// public float ThresholdLimit { get; set; } - /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - { - Rectangle intersect = Rectangle.Intersect(sourceRectangle, source.Bounds()); - - // Used ushort because the values should never exceed max ushort value - ushort startY = (ushort)intersect.Y; - ushort endY = (ushort)intersect.Bottom; - ushort startX = (ushort)intersect.X; - ushort endX = (ushort)intersect.Right; - - ushort width = (ushort)intersect.Width; - ushort height = (ushort)intersect.Height; - - // ClusterSize defines the size of cluster to used to check for average. Tweaked to support upto 4k wide pixels and not more. 4096 / 16 is 256 thus the '-1' - byte clusterSize = (byte)Math.Truncate((width / 16f) - 1); - - // Using pooled 2d buffer for integer image table and temp memory to hold Rgb24 converted pixel data - using (Buffer2D intImage = configuration.MemoryAllocator.Allocate2D(width, height)) - using (IMemoryOwner tmpBuffer = configuration.MemoryAllocator.Allocate(width * height)) - { - // Defines the rectangle section of the image to work on - Rectangle workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); - - this.pixelOpInstance.ToRgb24(source.GetPixelSpan(), tmpBuffer.GetSpan()); - - for (ushort x = startX; x < endX; x++) - { - Span rgbSpan = tmpBuffer.GetSpan(); - ulong sum = 0; - for (ushort y = startY; y < endY; y++) - { - ref Rgb24 rgb = ref rgbSpan[(width * y) + x]; - - sum += (ulong)(rgb.R + rgb.G + rgb.G); - if (x - startX != 0) - { - intImage[x - startX, y - startY] = intImage[x - startX - 1, y - startY] + sum; - } - else - { - intImage[x - startX, y - startY] = sum; - } - } - } - - ParallelHelper.IterateRows( - workingRectangle, - configuration, - rows => - { - Span rgbSpan = tmpBuffer.GetSpan(); - ushort x1, y1, x2, y2; - uint count = 0; - long sum = 0; - - for (ushort x = startX; x < endX; x++) - { - for (ushort y = (ushort)rows.Min; y < (ushort)rows.Max; y++) - { - ref Rgb24 rgb = ref rgbSpan[(width * y) + x]; - - x1 = (ushort)Math.Max(x - startX - clusterSize + 1, 0); - x2 = (ushort)Math.Min(x - startX + clusterSize + 1, width - 1); - y1 = (ushort)Math.Max(y - startY - clusterSize + 1, 0); - y2 = (ushort)Math.Min(y - startY + clusterSize + 1, height - 1); - - count = (uint)((x2 - x1) * (y2 - y1)); - sum = (long)Math.Min(intImage[x2, y2] - intImage[x1, y2] - intImage[x2, y1] + intImage[x1, y1], long.MaxValue); - - if ((rgb.R + rgb.G + rgb.B) * count <= sum * this.ThresholdLimit) - { - source[x, y] = this.Lower; - } - else - { - source[x, y] = this.Upper; - } - } - } - }); - } - } + /// + public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) + where TPixel : unmanaged, IPixel + => new AdaptiveThresholdProcessor(configuration, this, source, sourceRectangle); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs new file mode 100644 index 000000000..130fc40f3 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs @@ -0,0 +1,168 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; +using System.Runtime.CompilerServices; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors.Binarization +{ + /// + /// Performs Bradley Adaptive Threshold filter against an image. + /// + internal class AdaptiveThresholdProcessor : ImageProcessor + where TPixel : unmanaged, IPixel + { + private readonly AdaptiveThresholdProcessor definition; + private readonly PixelOperations pixelOpInstance; + + /// + /// Initializes a new instance of the class. + /// + /// The configuration which allows altering default behaviour or extending the library. + /// The defining the processor parameters. + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + public AdaptiveThresholdProcessor(Configuration configuration, AdaptiveThresholdProcessor definition, Image source, Rectangle sourceRectangle) + : base(configuration, source, sourceRectangle) + { + this.pixelOpInstance = PixelOperations.Instance; + this.definition = definition; + } + + /// + protected override void OnFrameApply(ImageFrame source) + { + var intersect = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); + + Configuration configuration = this.Configuration; + TPixel upper = this.definition.Upper.ToPixel(); + TPixel lower = this.definition.Lower.ToPixel(); + float thresholdLimit = this.definition.ThresholdLimit; + + // Used ushort because the values should never exceed max ushort value. + ushort startY = (ushort)intersect.Y; + ushort endY = (ushort)intersect.Bottom; + ushort startX = (ushort)intersect.X; + ushort endX = (ushort)intersect.Right; + + ushort width = (ushort)intersect.Width; + ushort height = (ushort)intersect.Height; + + // ClusterSize defines the size of cluster to used to check for average. Tweaked to support up to 4k wide pixels and not more. 4096 / 16 is 256 thus the '-1' + byte clusterSize = (byte)Math.Truncate((width / 16f) - 1); + + // Using pooled 2d buffer for integer image table and temp memory to hold Rgb24 converted pixel data. + using (Buffer2D intImage = this.Configuration.MemoryAllocator.Allocate2D(width, height)) + using (IMemoryOwner tmpBuffer = this.Configuration.MemoryAllocator.Allocate(width * height)) + { + // Defines the rectangle section of the image to work on. + var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); + + this.pixelOpInstance.ToRgb24(this.Configuration, source.GetPixelSpan(), tmpBuffer.GetSpan()); + + for (ushort x = startX; x < endX; x++) + { + Span rgbSpan = tmpBuffer.GetSpan(); + ulong sum = 0; + for (ushort y = startY; y < endY; y++) + { + ref Rgb24 rgb = ref rgbSpan[(width * y) + x]; + + sum += (ulong)(rgb.R + rgb.G + rgb.G); + if (x - startX != 0) + { + intImage[x - startX, y - startY] = intImage[x - startX - 1, y - startY] + sum; + } + else + { + intImage[x - startX, y - startY] = sum; + } + } + } + + var operation = new RowOperation(workingRectangle, source, tmpBuffer, intImage, upper, lower, thresholdLimit, clusterSize, startX, endX, startY); + ParallelRowIterator.IterateRows( + configuration, + workingRectangle, + in operation); + } + } + + private readonly struct RowOperation : IRowOperation + { + private readonly Rectangle bounds; + private readonly ImageFrame source; + private readonly IMemoryOwner tmpBuffer; + private readonly Buffer2D intImage; + private readonly TPixel upper; + private readonly TPixel lower; + private readonly float thresholdLimit; + private readonly ushort startX; + private readonly ushort endX; + private readonly ushort startY; + private readonly byte clusterSize; + + [MethodImpl(InliningOptions.ShortMethod)] + public RowOperation( + Rectangle bounds, + ImageFrame source, + IMemoryOwner tmpBuffer, + Buffer2D intImage, + TPixel upper, + TPixel lower, + float thresholdLimit, + byte clusterSize, + ushort startX, + ushort endX, + ushort startY) + { + this.bounds = bounds; + this.source = source; + this.tmpBuffer = tmpBuffer; + this.intImage = intImage; + this.upper = upper; + this.lower = lower; + this.thresholdLimit = thresholdLimit; + this.startX = startX; + this.endX = endX; + this.startY = startY; + this.clusterSize = clusterSize; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void Invoke(int y) + { + Span rgbSpan = this.tmpBuffer.GetSpan(); + ushort x1, y1, x2, y2; + + for (ushort x = this.startX; x < this.endX; x++) + { + ref Rgb24 rgb = ref rgbSpan[(this.bounds.Width * y) + x]; + + x1 = (ushort)Math.Max(x - this.startX - this.clusterSize + 1, 0); + x2 = (ushort)Math.Min(x - this.startX + this.clusterSize + 1, this.bounds.Width - 1); + y1 = (ushort)Math.Max(y - this.startY - this.clusterSize + 1, 0); + y2 = (ushort)Math.Min(y - this.startY + this.clusterSize + 1, this.bounds.Height - 1); + + var count = (uint)((x2 - x1) * (y2 - y1)); + var sum = (long)Math.Min(this.intImage[x2, y2] - this.intImage[x1, y2] - this.intImage[x2, y1] + this.intImage[x1, y1], long.MaxValue); + + if ((rgb.R + rgb.G + rgb.B) * count <= sum * this.thresholdLimit) + { + this.source[x, y] = this.lower; + } + else + { + this.source[x, y] = this.upper; + } + } + } + } + } +} From 4d3d0fb2f5f1104274a07e9b17df6d2a75e7bfa7 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Thu, 2 Apr 2020 15:18:53 +0200 Subject: [PATCH 52/58] Add tests for the AdaptiveThreshold processor --- .../Binarization/AdaptiveThresholdTests.cs | 77 +++++++++++++++++++ tests/ImageSharp.Tests/TestImages.cs | 3 + tests/Images/External | 2 +- tests/Images/Input/Png/Bradley01.png | 3 + tests/Images/Input/Png/Bradley02.png | 3 + 5 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 tests/ImageSharp.Tests/Processing/Binarization/AdaptiveThresholdTests.cs create mode 100644 tests/Images/Input/Png/Bradley01.png create mode 100644 tests/Images/Input/Png/Bradley02.png diff --git a/tests/ImageSharp.Tests/Processing/Binarization/AdaptiveThresholdTests.cs b/tests/ImageSharp.Tests/Processing/Binarization/AdaptiveThresholdTests.cs new file mode 100644 index 000000000..309716eb5 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Binarization/AdaptiveThresholdTests.cs @@ -0,0 +1,77 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Formats.Tga; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Binarization; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Binarization +{ + public class AdaptiveThresholdTests : BaseImageOperationsExtensionTest + { + [Fact] + public void AdaptiveThreshold_UsesDefaults_Works() + { + var expectedThresholdLimit = .85f; + Color expectedUpper = Color.White; + Color expectedLower = Color.Black; + this.operations.AdaptiveThreshold(); + AdaptiveThresholdProcessor p = this.Verify(); + Assert.Equal(expectedThresholdLimit, p.ThresholdLimit); + Assert.Equal(expectedUpper, p.Upper); + Assert.Equal(expectedLower, p.Lower); + } + + [Fact] + public void AdaptiveThreshold_SettingThresholdLimit_Works() + { + var expectedThresholdLimit = .65f; + this.operations.AdaptiveThreshold(expectedThresholdLimit); + AdaptiveThresholdProcessor p = this.Verify(); + Assert.Equal(expectedThresholdLimit, p.ThresholdLimit); + Assert.Equal(Color.White, p.Upper); + Assert.Equal(Color.Black, p.Lower); + } + + [Fact] + public void AdaptiveThreshold_SettingUpperLowerThresholds_Works() + { + Color expectedUpper = Color.HotPink; + Color expectedLower = Color.Yellow; + this.operations.AdaptiveThreshold(expectedUpper, expectedLower); + AdaptiveThresholdProcessor p = this.Verify(); + Assert.Equal(expectedUpper, p.Upper); + Assert.Equal(expectedLower, p.Lower); + } + + [Fact] + public void AdaptiveThreshold_SettingUpperLowerWithThresholdLimit_Works() + { + var expectedThresholdLimit = .77f; + Color expectedUpper = Color.HotPink; + Color expectedLower = Color.Yellow; + this.operations.AdaptiveThreshold(expectedUpper, expectedLower, expectedThresholdLimit); + AdaptiveThresholdProcessor p = this.Verify(); + Assert.Equal(expectedThresholdLimit, p.ThresholdLimit); + Assert.Equal(expectedUpper, p.Upper); + Assert.Equal(expectedLower, p.Lower); + } + + [Theory] + [WithFile(TestImages.Png.Bradley01, PixelTypes.Rgba32)] + [WithFile(TestImages.Png.Bradley02, PixelTypes.Rgba32)] + public void AdaptiveThreshold_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(img => img.AdaptiveThreshold()); + image.DebugSave(provider); + image.CompareToReferenceOutput(ImageComparer.Exact, provider); + } + } + } +} diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 272998a89..e475a7712 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -83,6 +83,9 @@ namespace SixLabors.ImageSharp.Tests public const string Ducky = "Png/ducky.png"; public const string Rainbow = "Png/rainbow.png"; + public const string Bradley01 = "Png/Bradley01.png"; + public const string Bradley02 = "Png/Bradley02.png"; + // Issue 1014: https://github.com/SixLabors/ImageSharp/issues/1014 public const string Issue1014_1 = "Png/issues/Issue_1014_1.png"; public const string Issue1014_2 = "Png/issues/Issue_1014_2.png"; diff --git a/tests/Images/External b/tests/Images/External index fe694a393..c04c8b73a 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit fe694a3938bea3565071a96cb1c90c4cbc586ff9 +Subproject commit c04c8b73a99c1b198597ae640394d91ddd8e033b diff --git a/tests/Images/Input/Png/Bradley01.png b/tests/Images/Input/Png/Bradley01.png new file mode 100644 index 000000000..5af2913e6 --- /dev/null +++ b/tests/Images/Input/Png/Bradley01.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d7eddc690c9d50fcaca3b0045d225b08c2fb172ceff5eead1d476c4df0354d02 +size 25266 diff --git a/tests/Images/Input/Png/Bradley02.png b/tests/Images/Input/Png/Bradley02.png new file mode 100644 index 000000000..917bf9310 --- /dev/null +++ b/tests/Images/Input/Png/Bradley02.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a73ebf6e35d5336bdf194d5098bcbe0ad240bbd09cd357816aacb1e0e7e6a614 +size 26467 From 2bd2f7b0413083365971b1a1d61bfc402938b740 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Thu, 2 Apr 2020 17:25:46 +0200 Subject: [PATCH 53/58] Remove not needed tmp buffer --- .../AdaptiveThresholdProcessor{TPixel}.cs | 31 +++++++++---------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs index 130fc40f3..109631ab8 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs @@ -2,8 +2,8 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Buffers; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; @@ -58,20 +58,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization // Using pooled 2d buffer for integer image table and temp memory to hold Rgb24 converted pixel data. using (Buffer2D intImage = this.Configuration.MemoryAllocator.Allocate2D(width, height)) - using (IMemoryOwner tmpBuffer = this.Configuration.MemoryAllocator.Allocate(width * height)) { // Defines the rectangle section of the image to work on. var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); - this.pixelOpInstance.ToRgb24(this.Configuration, source.GetPixelSpan(), tmpBuffer.GetSpan()); - + Rgba32 rgb = default; for (ushort x = startX; x < endX; x++) { - Span rgbSpan = tmpBuffer.GetSpan(); ulong sum = 0; for (ushort y = startY; y < endY; y++) { - ref Rgb24 rgb = ref rgbSpan[(width * y) + x]; + Span row = source.GetPixelRowSpan(y); + ref TPixel rowRef = ref MemoryMarshal.GetReference(row); + ref TPixel color = ref Unsafe.Add(ref rowRef, x); + color.ToRgba32(ref rgb); sum += (ulong)(rgb.R + rgb.G + rgb.G); if (x - startX != 0) @@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization } } - var operation = new RowOperation(workingRectangle, source, tmpBuffer, intImage, upper, lower, thresholdLimit, clusterSize, startX, endX, startY); + var operation = new RowOperation(workingRectangle, source, intImage, upper, lower, thresholdLimit, clusterSize, startX, endX, startY); ParallelRowIterator.IterateRows( configuration, workingRectangle, @@ -97,7 +97,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization { private readonly Rectangle bounds; private readonly ImageFrame source; - private readonly IMemoryOwner tmpBuffer; private readonly Buffer2D intImage; private readonly TPixel upper; private readonly TPixel lower; @@ -111,7 +110,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization public RowOperation( Rectangle bounds, ImageFrame source, - IMemoryOwner tmpBuffer, Buffer2D intImage, TPixel upper, TPixel lower, @@ -123,7 +121,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization { this.bounds = bounds; this.source = source; - this.tmpBuffer = tmpBuffer; this.intImage = intImage; this.upper = upper; this.lower = lower; @@ -138,17 +135,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization [MethodImpl(InliningOptions.ShortMethod)] public void Invoke(int y) { - Span rgbSpan = this.tmpBuffer.GetSpan(); - ushort x1, y1, x2, y2; + Rgba32 rgb = default; for (ushort x = this.startX; x < this.endX; x++) { - ref Rgb24 rgb = ref rgbSpan[(this.bounds.Width * y) + x]; + TPixel pixel = this.source.PixelBuffer[x, y]; + pixel.ToRgba32(ref rgb); - x1 = (ushort)Math.Max(x - this.startX - this.clusterSize + 1, 0); - x2 = (ushort)Math.Min(x - this.startX + this.clusterSize + 1, this.bounds.Width - 1); - y1 = (ushort)Math.Max(y - this.startY - this.clusterSize + 1, 0); - y2 = (ushort)Math.Min(y - this.startY + this.clusterSize + 1, this.bounds.Height - 1); + var x1 = (ushort)Math.Max(x - this.startX - this.clusterSize + 1, 0); + var x2 = (ushort)Math.Min(x - this.startX + this.clusterSize + 1, this.bounds.Width - 1); + var y1 = (ushort)Math.Max(y - this.startY - this.clusterSize + 1, 0); + var y2 = (ushort)Math.Min(y - this.startY + this.clusterSize + 1, this.bounds.Height - 1); var count = (uint)((x2 - x1) * (y2 - y1)); var sum = (long)Math.Min(this.intImage[x2, y2] - this.intImage[x1, y2] - this.intImage[x2, y1] + this.intImage[x1, y1], long.MaxValue); From c1fc52b676489a2f6caf58a78b06779e8961160f Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Thu, 2 Apr 2020 17:48:06 +0200 Subject: [PATCH 54/58] Changed startX and endX from ushort to int, Add test with rectangle --- .../AdaptiveThresholdProcessor{TPixel}.cs | 31 ++++++----- .../Binarization/AdaptiveThresholdTests.cs | 52 ++++++++++++++++++- tests/Images/External | 2 +- 3 files changed, 67 insertions(+), 18 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs index 109631ab8..6daf3a8ed 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs @@ -44,14 +44,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization TPixel lower = this.definition.Lower.ToPixel(); float thresholdLimit = this.definition.ThresholdLimit; - // Used ushort because the values should never exceed max ushort value. - ushort startY = (ushort)intersect.Y; - ushort endY = (ushort)intersect.Bottom; - ushort startX = (ushort)intersect.X; - ushort endX = (ushort)intersect.Right; + int startY = intersect.Y; + int endY = intersect.Bottom; + int startX = intersect.X; + int endX = intersect.Right; - ushort width = (ushort)intersect.Width; - ushort height = (ushort)intersect.Height; + int width = intersect.Width; + int height = intersect.Height; // ClusterSize defines the size of cluster to used to check for average. Tweaked to support up to 4k wide pixels and not more. 4096 / 16 is 256 thus the '-1' byte clusterSize = (byte)Math.Truncate((width / 16f) - 1); @@ -63,10 +62,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); Rgba32 rgb = default; - for (ushort x = startX; x < endX; x++) + for (int x = startX; x < endX; x++) { ulong sum = 0; - for (ushort y = startY; y < endY; y++) + for (int y = startY; y < endY; y++) { Span row = source.GetPixelRowSpan(y); ref TPixel rowRef = ref MemoryMarshal.GetReference(row); @@ -101,9 +100,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization private readonly TPixel upper; private readonly TPixel lower; private readonly float thresholdLimit; - private readonly ushort startX; - private readonly ushort endX; - private readonly ushort startY; + private readonly int startX; + private readonly int endX; + private readonly int startY; private readonly byte clusterSize; [MethodImpl(InliningOptions.ShortMethod)] @@ -115,9 +114,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization TPixel lower, float thresholdLimit, byte clusterSize, - ushort startX, - ushort endX, - ushort startY) + int startX, + int endX, + int startY) { this.bounds = bounds; this.source = source; @@ -137,7 +136,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization { Rgba32 rgb = default; - for (ushort x = this.startX; x < this.endX; x++) + for (int x = this.startX; x < this.endX; x++) { TPixel pixel = this.source.PixelBuffer[x, y]; pixel.ToRgba32(ref rgb); diff --git a/tests/ImageSharp.Tests/Processing/Binarization/AdaptiveThresholdTests.cs b/tests/ImageSharp.Tests/Processing/Binarization/AdaptiveThresholdTests.cs index 309716eb5..f992ac35b 100644 --- a/tests/ImageSharp.Tests/Processing/Binarization/AdaptiveThresholdTests.cs +++ b/tests/ImageSharp.Tests/Processing/Binarization/AdaptiveThresholdTests.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Formats.Tga; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Binarization; @@ -15,10 +14,15 @@ namespace SixLabors.ImageSharp.Tests.Processing.Binarization [Fact] public void AdaptiveThreshold_UsesDefaults_Works() { + // arrange var expectedThresholdLimit = .85f; Color expectedUpper = Color.White; Color expectedLower = Color.Black; + + // act this.operations.AdaptiveThreshold(); + + // assert AdaptiveThresholdProcessor p = this.Verify(); Assert.Equal(expectedThresholdLimit, p.ThresholdLimit); Assert.Equal(expectedUpper, p.Upper); @@ -28,8 +32,13 @@ namespace SixLabors.ImageSharp.Tests.Processing.Binarization [Fact] public void AdaptiveThreshold_SettingThresholdLimit_Works() { + // arrange var expectedThresholdLimit = .65f; + + // act this.operations.AdaptiveThreshold(expectedThresholdLimit); + + // assert AdaptiveThresholdProcessor p = this.Verify(); Assert.Equal(expectedThresholdLimit, p.ThresholdLimit); Assert.Equal(Color.White, p.Upper); @@ -39,9 +48,14 @@ namespace SixLabors.ImageSharp.Tests.Processing.Binarization [Fact] public void AdaptiveThreshold_SettingUpperLowerThresholds_Works() { + // arrange Color expectedUpper = Color.HotPink; Color expectedLower = Color.Yellow; + + // act this.operations.AdaptiveThreshold(expectedUpper, expectedLower); + + // assert AdaptiveThresholdProcessor p = this.Verify(); Assert.Equal(expectedUpper, p.Upper); Assert.Equal(expectedLower, p.Lower); @@ -50,16 +64,39 @@ namespace SixLabors.ImageSharp.Tests.Processing.Binarization [Fact] public void AdaptiveThreshold_SettingUpperLowerWithThresholdLimit_Works() { + // arrange var expectedThresholdLimit = .77f; Color expectedUpper = Color.HotPink; Color expectedLower = Color.Yellow; + + // act this.operations.AdaptiveThreshold(expectedUpper, expectedLower, expectedThresholdLimit); + + // assert AdaptiveThresholdProcessor p = this.Verify(); Assert.Equal(expectedThresholdLimit, p.ThresholdLimit); Assert.Equal(expectedUpper, p.Upper); Assert.Equal(expectedLower, p.Lower); } + [Fact] + public void AdaptiveThreshold_SettingUpperLowerWithThresholdLimit_WithRectangle_Works() + { + // arrange + var expectedThresholdLimit = .77f; + Color expectedUpper = Color.HotPink; + Color expectedLower = Color.Yellow; + + // act + this.operations.AdaptiveThreshold(expectedUpper, expectedLower, expectedThresholdLimit, this.rect); + + // assert + AdaptiveThresholdProcessor p = this.Verify(this.rect); + Assert.Equal(expectedThresholdLimit, p.ThresholdLimit); + Assert.Equal(expectedUpper, p.Upper); + Assert.Equal(expectedLower, p.Lower); + } + [Theory] [WithFile(TestImages.Png.Bradley01, PixelTypes.Rgba32)] [WithFile(TestImages.Png.Bradley02, PixelTypes.Rgba32)] @@ -73,5 +110,18 @@ namespace SixLabors.ImageSharp.Tests.Processing.Binarization image.CompareToReferenceOutput(ImageComparer.Exact, provider); } } + + [Theory] + [WithFile(TestImages.Png.Bradley02, PixelTypes.Rgba32)] + public void AdaptiveThreshold_WithRectangle_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(img => img.AdaptiveThreshold(Color.White, Color.Black, new Rectangle(60, 90, 200, 30))); + image.DebugSave(provider); + image.CompareToReferenceOutput(ImageComparer.Exact, provider); + } + } } } diff --git a/tests/Images/External b/tests/Images/External index c04c8b73a..6fdc6d19b 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit c04c8b73a99c1b198597ae640394d91ddd8e033b +Subproject commit 6fdc6d19b101dc1c00a297d3e92257df60c413d0 From d032998b1cf50b1422f4c0cef273fbca66f4a767 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Thu, 2 Apr 2020 19:59:24 +0200 Subject: [PATCH 55/58] Using pixel row span to access the pixels --- .../Binarization/AdaptiveThresholdProcessor{TPixel}.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs index 6daf3a8ed..7b3e10b0d 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs @@ -135,10 +135,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization public void Invoke(int y) { Rgba32 rgb = default; + Span pixelRow = this.source.GetPixelRowSpan(y); for (int x = this.startX; x < this.endX; x++) { - TPixel pixel = this.source.PixelBuffer[x, y]; + TPixel pixel = pixelRow[x]; pixel.ToRgba32(ref rgb); var x1 = (ushort)Math.Max(x - this.startX - this.clusterSize + 1, 0); From ca2ed0d68285d72800a4e6a155308aac380c79c6 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Thu, 2 Apr 2020 23:15:48 +0200 Subject: [PATCH 56/58] Review changes --- .../AdaptiveThresholdProcessor{TPixel}.cs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs index 7b3e10b0d..dd8833ad9 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs @@ -18,7 +18,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization where TPixel : unmanaged, IPixel { private readonly AdaptiveThresholdProcessor definition; - private readonly PixelOperations pixelOpInstance; /// /// Initializes a new instance of the class. @@ -30,7 +29,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization public AdaptiveThresholdProcessor(Configuration configuration, AdaptiveThresholdProcessor definition, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) { - this.pixelOpInstance = PixelOperations.Instance; this.definition = definition; } @@ -58,9 +56,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization // Using pooled 2d buffer for integer image table and temp memory to hold Rgb24 converted pixel data. using (Buffer2D intImage = this.Configuration.MemoryAllocator.Allocate2D(width, height)) { - // Defines the rectangle section of the image to work on. - var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); - Rgba32 rgb = default; for (int x = startX; x < endX; x++) { @@ -84,10 +79,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization } } - var operation = new RowOperation(workingRectangle, source, intImage, upper, lower, thresholdLimit, clusterSize, startX, endX, startY); + var operation = new RowOperation(intersect, source, intImage, upper, lower, thresholdLimit, clusterSize, startX, endX, startY); ParallelRowIterator.IterateRows( configuration, - workingRectangle, + intersect, in operation); } } @@ -142,10 +137,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization TPixel pixel = pixelRow[x]; pixel.ToRgba32(ref rgb); - var x1 = (ushort)Math.Max(x - this.startX - this.clusterSize + 1, 0); - var x2 = (ushort)Math.Min(x - this.startX + this.clusterSize + 1, this.bounds.Width - 1); - var y1 = (ushort)Math.Max(y - this.startY - this.clusterSize + 1, 0); - var y2 = (ushort)Math.Min(y - this.startY + this.clusterSize + 1, this.bounds.Height - 1); + var x1 = Math.Max(x - this.startX - this.clusterSize + 1, 0); + var x2 = Math.Min(x - this.startX + this.clusterSize + 1, this.bounds.Width - 1); + var y1 = Math.Max(y - this.startY - this.clusterSize + 1, 0); + var y2 = Math.Min(y - this.startY + this.clusterSize + 1, this.bounds.Height - 1); var count = (uint)((x2 - x1) * (y2 - y1)); var sum = (long)Math.Min(this.intImage[x2, y2] - this.intImage[x1, y2] - this.intImage[x2, y1] + this.intImage[x1, y1], long.MaxValue); From 3df1471af6c738f808d808eabee3a11a4002661b Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 3 Apr 2020 11:48:15 +0200 Subject: [PATCH 57/58] Minor formatting change --- src/ImageSharp/Processing/AdaptiveThresholdExtensions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/Processing/AdaptiveThresholdExtensions.cs b/src/ImageSharp/Processing/AdaptiveThresholdExtensions.cs index 4fc78a958..3279d96e3 100644 --- a/src/ImageSharp/Processing/AdaptiveThresholdExtensions.cs +++ b/src/ImageSharp/Processing/AdaptiveThresholdExtensions.cs @@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image this method extends. /// Upper (white) color for thresholding. - /// Lower (black) color for thresholding + /// Lower (black) color for thresholding. /// Rectangle region to apply the processor on. /// The . public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, Color upper, Color lower, Rectangle rectangle) @@ -64,7 +64,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image this method extends. /// Upper (white) color for thresholding. - /// Lower (black) color for thresholding + /// Lower (black) color for thresholding. /// Threshold limit (0.0-1.0) to consider for binarization. /// Rectangle region to apply the processor on. /// The . From 79cef659f42ab6c4c5d6d6b64a3624466a72194f Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 5 Apr 2020 13:30:01 +0200 Subject: [PATCH 58/58] For paletted Bgra5551 alpha bits are not ignored --- src/ImageSharp/Formats/Tga/TgaDecoderCore.cs | 27 ++++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs index 7753b916d..816a472fb 100644 --- a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs +++ b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs @@ -252,14 +252,14 @@ namespace SixLabors.ImageSharp.Formats.Tga { for (int x = width - 1; x >= 0; x--) { - this.ReadPalettedBgr16Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow); + this.ReadPalettedBgra16Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow); } } else { for (int x = 0; x < width; x++) { - this.ReadPalettedBgr16Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow); + this.ReadPalettedBgra16Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow); } } @@ -338,11 +338,7 @@ namespace SixLabors.ImageSharp.Formats.Tga color.FromL8(Unsafe.As(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes])); break; case 2: - Bgra5551 bgra = Unsafe.As(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes]); - - // Set alpha value to 1, to treat it as opaque for Bgra5551. - bgra.PackedValue = (ushort)(bgra.PackedValue | 0x8000); - color.FromBgra5551(bgra); + this.ReadPalettedBgra16Pixel(palette, bufferSpan[idx], colorMapPixelSizeInBytes, ref color); break; case 3: color.FromBgr24(Unsafe.As(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes])); @@ -723,20 +719,29 @@ namespace SixLabors.ImageSharp.Formats.Tga PixelOperations.Instance.FromBgra32Bytes(this.configuration, row.GetSpan(), pixelSpan, width); } - private void ReadPalettedBgr16Pixel(byte[] palette, int colorMapPixelSizeInBytes, int x, TPixel color, Span pixelRow) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ReadPalettedBgra16Pixel(byte[] palette, int colorMapPixelSizeInBytes, int x, TPixel color, Span pixelRow) where TPixel : unmanaged, IPixel { int colorIndex = this.currentStream.ReadByte(); + this.ReadPalettedBgra16Pixel(palette, colorIndex, colorMapPixelSizeInBytes, ref color); + pixelRow[x] = color; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ReadPalettedBgra16Pixel(byte[] palette, int index, int colorMapPixelSizeInBytes, ref TPixel color) + where TPixel : unmanaged, IPixel + { Bgra5551 bgra = default; - bgra.FromBgra5551(Unsafe.As(ref palette[colorIndex * colorMapPixelSizeInBytes])); + bgra.FromBgra5551(Unsafe.As(ref palette[index * colorMapPixelSizeInBytes])); + if (!this.hasAlpha) { - // Set alpha value to 1, to treat it as opaque for Bgra5551. + // Set alpha value to 1, to treat it as opaque. bgra.PackedValue = (ushort)(bgra.PackedValue | 0x8000); } color.FromBgra5551(bgra); - pixelRow[x] = color; } [MethodImpl(MethodImplOptions.AggressiveInlining)]